ukwm/0000775000175000017500000000000013260071761010466 5ustar fengfengukwm/ABOUT-NLS0000664000175000017500000027366513220600403011723 0ustar fengfeng1 Notes on the Free Translation Project *************************************** Free software is going international! The Free Translation Project is a way to get maintainers of free software, translators, and users all together, so that free software will gradually become able to speak many languages. A few packages already provide translations for their messages. If you found this `ABOUT-NLS' file inside a distribution, you may assume that the distributed package does use GNU `gettext' internally, itself available at your nearest GNU archive site. But you do _not_ need to install GNU `gettext' prior to configuring, installing or using this package with messages translated. Installers will find here some useful hints. These notes also explain how users should proceed for getting the programs to use the available translations. They tell how people wanting to contribute and work on translations can contact the appropriate team. 1.1 INSTALL Matters =================== Some packages are "localizable" when properly installed; the programs they contain can be made to speak your own native language. Most such packages use GNU `gettext'. Other packages have their own ways to internationalization, predating GNU `gettext'. By default, this package will be installed to allow translation of messages. It will automatically detect whether the system already provides the GNU `gettext' functions. Installers may use special options at configuration time for changing the default behaviour. The command: ./configure --disable-nls will _totally_ disable translation of messages. When you already have GNU `gettext' installed on your system and run configure without an option for your new package, `configure' will probably detect the previously built and installed `libintl' library and will decide to use it. If not, you may have to to use the `--with-libintl-prefix' option to tell `configure' where to look for it. Internationalized packages usually have many `po/LL.po' files, where LL gives an ISO 639 two-letter code identifying the language. Unless translations have been forbidden at `configure' time by using the `--disable-nls' switch, all available translations are installed together with the package. However, the environment variable `LINGUAS' may be set, prior to configuration, to limit the installed set. `LINGUAS' should then contain a space separated list of two-letter codes, stating which languages are allowed. 1.2 Using This Package ====================== As a user, if your language has been installed for this package, you only have to set the `LANG' environment variable to the appropriate `LL_CC' combination. If you happen to have the `LC_ALL' or some other `LC_xxx' environment variables set, you should unset them before setting `LANG', otherwise the setting of `LANG' will not have the desired effect. Here `LL' is an ISO 639 two-letter language code, and `CC' is an ISO 3166 two-letter country code. For example, let's suppose that you speak German and live in Germany. At the shell prompt, merely execute `setenv LANG de_DE' (in `csh'), `export LANG; LANG=de_DE' (in `sh') or `export LANG=de_DE' (in `bash'). This can be done from your `.login' or `.profile' file, once and for all. You might think that the country code specification is redundant. But in fact, some languages have dialects in different countries. For example, `de_AT' is used for Austria, and `pt_BR' for Brazil. The country code serves to distinguish the dialects. The locale naming convention of `LL_CC', with `LL' denoting the language and `CC' denoting the country, is the one use on systems based on GNU libc. On other systems, some variations of this scheme are used, such as `LL' or `LL_CC.ENCODING'. You can get the list of locales supported by your system for your language by running the command `locale -a | grep '^LL''. Not all programs have translations for all languages. By default, an English message is shown in place of a nonexistent translation. If you understand other languages, you can set up a priority list of languages. This is done through a different environment variable, called `LANGUAGE'. GNU `gettext' gives preference to `LANGUAGE' over `LANG' for the purpose of message handling, but you still need to have `LANG' set to the primary language; this is required by other parts of the system libraries. For example, some Swedish users who would rather read translations in German than English for when Swedish is not available, set `LANGUAGE' to `sv:de' while leaving `LANG' to `sv_SE'. Special advice for Norwegian users: The language code for Norwegian bokma*l changed from `no' to `nb' recently (in 2003). During the transition period, while some message catalogs for this language are installed under `nb' and some older ones under `no', it's recommended for Norwegian users to set `LANGUAGE' to `nb:no' so that both newer and older translations are used. In the `LANGUAGE' environment variable, but not in the `LANG' environment variable, `LL_CC' combinations can be abbreviated as `LL' to denote the language's main dialect. For example, `de' is equivalent to `de_DE' (German as spoken in Germany), and `pt' to `pt_PT' (Portuguese as spoken in Portugal) in this context. 1.3 Translating Teams ===================== For the Free Translation Project to be a success, we need interested people who like their own language and write it well, and who are also able to synergize with other translators speaking the same language. Each translation team has its own mailing list. The up-to-date list of teams can be found at the Free Translation Project's homepage, `http://translationproject.org/', in the "Teams" area. If you'd like to volunteer to _work_ at translating messages, you should become a member of the translating team for your own language. The subscribing address is _not_ the same as the list itself, it has `-request' appended. For example, speakers of Swedish can send a message to `sv-request@li.org', having this message body: subscribe Keep in mind that team members are expected to participate _actively_ in translations, or at solving translational difficulties, rather than merely lurking around. If your team does not exist yet and you want to start one, or if you are unsure about what to do or how to get started, please write to `coordinator@translationproject.org' to reach the coordinator for all translator teams. The English team is special. It works at improving and uniformizing the terminology in use. Proven linguistic skills are praised more than programming skills, here. 1.4 Available Packages ====================== Languages are not equally supported in all packages. The following matrix shows the current state of internationalization, as of June 2010. The matrix shows, in regard of each package, for which languages PO files have been submitted to translation coordination, with a translation percentage of at least 50%. Ready PO files af am an ar as ast az be bg bn bn_IN bs ca crh cs +---------------------------------------------------+ a2ps | [] [] [] | aegis | | anubis | | aspell | [] [] [] | bash | [] [] [] | bfd | | binutils | [] | bison | | bison-runtime | [] | buzztrax | [] | ccd2cue | | ccide | | cflow | | clisp | | coreutils | [] [] | cpio | | cppi | | cpplib | [] | cryptsetup | [] | datamash | | denemo | [] [] | dfarc | [] | dialog | [] [] [] | dico | | diffutils | [] | dink | [] | direvent | | doodle | [] | dos2unix | | dos2unix-man | | e2fsprogs | [] [] | enscript | [] | exif | [] | fetchmail | [] [] | findutils | [] | flex | [] | freedink | [] [] | fusionforge | | gas | | gawk | [] | gcal | [] | gcc | | gdbm | | gettext-examples | [] [] [] [] [] | gettext-runtime | [] [] [] | gettext-tools | [] [] | gip | [] [] | gjay | | glunarclock | [] [] [] | gnubiff | [] | gnubik | [] | gnucash | () () [] | gnuchess | | gnulib | [] | gnunet | | gnunet-gtk | | gold | | gphoto2 | [] | gprof | [] | gpsdrive | | gramadoir | | grep | [] [] | grub | [] | gsasl | | gss | | gst-plugins-bad | [] | gst-plugins-base | [] [] [] | gst-plugins-good | [] [] [] | gst-plugins-ugly | [] [] [] | gstreamer | [] [] [] [] | gtick | [] | gtkam | [] [] | gtkorphan | [] [] | gtkspell | [] [] [] [] [] | guix | | guix-packages | | gutenprint | [] | hello | [] | help2man | | help2man-texi | | hylafax | | idutils | | iso_15924 | [] | iso_3166 | [] [] [] [] [] [] [] [] [] [] | iso_3166_2 | | iso_4217 | [] | iso_639 | [] [] [] [] [] [] [] [] [] | iso_639_3 | [] [] | iso_639_5 | | jwhois | | kbd | [] | klavaro | [] [] [] [] [] | latrine | | ld | [] | leafpad | [] [] [] [] | libc | [] [] [] | libexif | () | libextractor | | libgnutls | [] | libgpg-error | [] | libgphoto2 | [] | libgphoto2_port | [] | libgsasl | | libiconv | [] [] | libidn | [] | liferea | [] [] [] [] | lilypond | [] [] | lordsawar | [] | lprng | | lynx | [] [] | m4 | [] | mailfromd | | mailutils | | make | [] | man-db | [] [] | man-db-manpages | | midi-instruments | [] [] [] | minicom | [] | mkisofs | [] | myserver | [] | nano | [] [] [] | opcodes | | parted | [] | pies | | popt | [] | procps-ng | | procps-ng-man | | psmisc | [] | pspp | [] | pushover | [] | pwdutils | | pyspread | | radius | [] | recode | [] [] [] | recutils | | rpm | | rush | | sarg | | sed | [] [] [] | sharutils | [] | shishi | | skribilo | | solfege | [] | solfege-manual | | spotmachine | | sudo | [] [] | sudoers | [] [] | sysstat | [] | tar | [] [] [] | texinfo | [] [] | texinfo_document | [] | tigervnc | [] | tin | | tin-man | | tracgoogleappsa... | | trader | | util-linux | [] | ve | | vice | | vmm | | vorbis-tools | [] | wastesedge | | wcd | | wcd-man | | wdiff | [] [] | wget | [] [] | wyslij-po | | xboard | | xdg-user-dirs | [] [] [] [] [] [] [] [] [] [] | xkeyboard-config | [] [] [] | +---------------------------------------------------+ af am an ar as ast az be bg bn bn_IN bs ca crh cs 4 0 2 4 3 11 0 8 24 3 3 1 55 4 76 da de el en en_GB en_ZA eo es et eu fa fi fr +--------------------------------------------------+ a2ps | [] [] [] [] [] [] [] [] [] | aegis | [] [] [] [] | anubis | [] [] [] [] [] | aspell | [] [] [] [] [] [] [] | bash | [] [] [] [] [] | bfd | [] [] [] [] | binutils | [] [] [] | bison | [] [] [] [] [] [] [] [] | bison-runtime | [] [] [] [] [] [] [] [] | buzztrax | [] [] [] [] | ccd2cue | [] [] [] | ccide | [] [] [] [] [] [] | cflow | [] [] [] [] [] | clisp | [] [] [] [] [] | coreutils | [] [] [] [] [] | cpio | [] [] [] [] [] | cppi | [] [] [] [] [] | cpplib | [] [] [] [] [] [] | cryptsetup | [] [] [] [] [] | datamash | [] [] [] [] | denemo | | dfarc | [] [] [] [] [] [] | dialog | [] [] [] [] [] [] [] [] [] | dico | [] [] [] [] | diffutils | [] [] [] [] [] [] | dink | [] [] [] [] [] [] | direvent | [] [] [] [] | doodle | [] [] [] [] | dos2unix | [] [] [] [] [] | dos2unix-man | [] [] [] | e2fsprogs | [] [] [] [] [] | enscript | [] [] [] [] [] [] | exif | [] [] [] [] [] [] | fetchmail | [] () [] [] [] [] [] | findutils | [] [] [] [] [] [] [] [] | flex | [] [] [] [] [] [] | freedink | [] [] [] [] [] [] [] [] | fusionforge | [] [] [] | gas | [] [] [] | gawk | [] [] [] [] [] | gcal | [] [] [] [] | gcc | [] [] | gdbm | [] [] [] [] [] | gettext-examples | [] [] [] [] [] [] [] | gettext-runtime | [] [] [] [] [] [] | gettext-tools | [] [] [] [] [] | gip | [] [] [] [] [] [] [] | gjay | [] [] [] [] | glunarclock | [] [] [] [] [] | gnubiff | () [] [] () | gnubik | [] [] [] [] [] | gnucash | [] () () () () () () | gnuchess | [] [] [] [] | gnulib | [] [] [] [] [] [] [] | gnunet | [] | gnunet-gtk | [] | gold | [] [] [] | gphoto2 | [] () [] [] | gprof | [] [] [] [] [] [] | gpsdrive | [] [] [] [] | gramadoir | [] [] [] [] [] | grep | [] [] [] [] [] [] [] | grub | [] [] [] [] [] | gsasl | [] [] [] [] [] | gss | [] [] [] [] [] | gst-plugins-bad | [] [] | gst-plugins-base | [] [] [] [] [] [] | gst-plugins-good | [] [] [] [] [] [] [] | gst-plugins-ugly | [] [] [] [] [] [] [] [] | gstreamer | [] [] [] [] [] [] [] | gtick | [] () [] [] [] | gtkam | [] () [] [] [] [] | gtkorphan | [] [] [] [] [] | gtkspell | [] [] [] [] [] [] [] [] | guix | [] [] [] | guix-packages | | gutenprint | [] [] [] [] | hello | [] [] [] [] [] [] [] [] | help2man | [] [] [] [] [] [] [] | help2man-texi | [] [] [] | hylafax | [] [] | idutils | [] [] [] [] [] | iso_15924 | [] () [] [] () [] () | iso_3166 | [] () [] [] [] [] () [] () | iso_3166_2 | [] () () () | iso_4217 | [] () [] [] [] () [] () | iso_639 | [] () [] [] () [] () | iso_639_3 | () () () | iso_639_5 | () () () | jwhois | [] [] [] [] [] | kbd | [] [] [] [] [] [] | klavaro | [] [] [] [] [] [] [] | latrine | [] () [] [] | ld | [] [] [] [] | leafpad | [] [] [] [] [] [] [] [] | libc | [] [] [] [] [] | libexif | [] [] () [] [] | libextractor | [] | libgnutls | [] [] [] [] | libgpg-error | [] [] [] | libgphoto2 | [] () [] | libgphoto2_port | [] () [] [] [] [] | libgsasl | [] [] [] [] [] | libiconv | [] [] [] [] [] [] [] | libidn | [] [] [] [] [] | liferea | [] () [] [] [] [] [] | lilypond | [] [] [] [] [] [] | lordsawar | [] [] | lprng | | lynx | [] [] [] [] [] [] | m4 | [] [] [] [] [] [] | mailfromd | [] | mailutils | [] [] [] [] | make | [] [] [] [] [] | man-db | [] [] [] [] | man-db-manpages | [] [] | midi-instruments | [] [] [] [] [] [] [] [] [] | minicom | [] [] [] [] [] | mkisofs | [] [] [] | myserver | [] [] [] [] | nano | [] [] [] [] [] [] [] | opcodes | [] [] [] [] [] | parted | [] [] [] | pies | [] | popt | [] [] [] [] [] [] | procps-ng | [] [] | procps-ng-man | [] [] | psmisc | [] [] [] [] [] [] [] | pspp | [] [] [] | pushover | () [] [] [] | pwdutils | [] [] [] | pyspread | [] [] | radius | [] [] | recode | [] [] [] [] [] [] [] | recutils | [] [] [] [] | rpm | [] [] [] [] [] | rush | [] [] [] | sarg | [] [] | sed | [] [] [] [] [] [] [] [] | sharutils | [] [] [] [] | shishi | [] [] [] | skribilo | [] [] | solfege | [] [] [] [] [] [] [] [] | solfege-manual | [] [] [] [] [] | spotmachine | [] [] [] [] | sudo | [] [] [] [] [] [] | sudoers | [] [] [] [] [] [] | sysstat | [] [] [] [] [] [] | tar | [] [] [] [] [] [] [] | texinfo | [] [] [] [] [] | texinfo_document | [] [] [] [] | tigervnc | [] [] [] [] [] [] | tin | [] [] [] [] | tin-man | [] | tracgoogleappsa... | [] [] [] [] [] | trader | [] [] [] [] [] [] | util-linux | [] [] [] [] | ve | [] [] [] [] [] | vice | () () () | vmm | [] [] | vorbis-tools | [] [] [] [] | wastesedge | [] () | wcd | [] [] [] [] | wcd-man | [] | wdiff | [] [] [] [] [] [] [] | wget | [] [] [] [] [] [] | wyslij-po | [] [] [] [] | xboard | [] [] [] [] | xdg-user-dirs | [] [] [] [] [] [] [] [] [] [] | xkeyboard-config | [] [] [] [] [] [] [] | +--------------------------------------------------+ da de el en en_GB en_ZA eo es et eu fa fi fr 123 134 32 1 6 0 97 97 23 14 4 106 139 ga gd gl gu he hi hr hu hy ia id is it ja ka kk +-------------------------------------------------+ a2ps | [] [] [] [] | aegis | [] | anubis | [] [] [] [] | aspell | [] [] [] [] [] | bash | [] [] [] [] | bfd | [] [] | binutils | [] [] [] | bison | [] | bison-runtime | [] [] [] [] [] [] [] [] | buzztrax | | ccd2cue | [] | ccide | [] [] | cflow | [] [] [] | clisp | | coreutils | [] [] [] | cpio | [] [] [] [] [] [] | cppi | [] [] [] [] [] | cpplib | [] [] | cryptsetup | [] | datamash | | denemo | [] | dfarc | [] [] [] | dialog | [] [] [] [] [] [] [] [] [] [] | dico | | diffutils | [] [] [] [] | dink | [] | direvent | [] | doodle | [] [] | dos2unix | [] [] | dos2unix-man | | e2fsprogs | [] | enscript | [] [] [] | exif | [] [] [] [] [] [] | fetchmail | [] [] [] | findutils | [] [] [] [] [] [] [] | flex | [] | freedink | [] [] [] [] | fusionforge | | gas | [] | gawk | [] () [] | gcal | | gcc | | gdbm | | gettext-examples | [] [] [] [] [] [] [] | gettext-runtime | [] [] [] [] [] [] [] | gettext-tools | [] [] [] | gip | [] [] [] [] [] [] | gjay | [] | glunarclock | [] [] [] [] [] [] | gnubiff | [] [] () | gnubik | [] [] [] | gnucash | () () () () () [] | gnuchess | | gnulib | [] [] [] [] [] | gnunet | | gnunet-gtk | | gold | [] [] | gphoto2 | [] [] [] [] | gprof | [] [] [] [] | gpsdrive | [] [] [] [] | gramadoir | [] [] [] | grep | [] [] [] [] [] [] [] | grub | [] [] [] | gsasl | [] [] [] [] [] | gss | [] [] [] [] [] | gst-plugins-bad | [] | gst-plugins-base | [] [] [] [] | gst-plugins-good | [] [] [] [] [] [] | gst-plugins-ugly | [] [] [] [] [] [] | gstreamer | [] [] [] [] [] | gtick | [] [] [] [] [] | gtkam | [] [] [] [] [] | gtkorphan | [] [] [] [] | gtkspell | [] [] [] [] [] [] [] [] [] [] | guix | [] | guix-packages | | gutenprint | [] [] [] | hello | [] [] [] [] [] | help2man | [] [] [] | help2man-texi | | hylafax | [] | idutils | [] [] | iso_15924 | [] [] [] [] [] [] | iso_3166 | [] [] [] [] [] [] [] [] [] [] [] [] [] | iso_3166_2 | [] [] | iso_4217 | [] [] [] [] [] [] | iso_639 | [] [] [] [] [] [] [] [] [] | iso_639_3 | [] [] | iso_639_5 | | jwhois | [] [] [] [] | kbd | [] [] [] | klavaro | [] [] [] [] | latrine | [] | ld | [] [] [] [] | leafpad | [] [] [] [] [] [] [] () | libc | [] [] [] [] [] | libexif | [] | libextractor | | libgnutls | [] | libgpg-error | [] [] [] | libgphoto2 | [] [] | libgphoto2_port | [] [] | libgsasl | [] [] [] [] | libiconv | [] [] [] [] [] [] [] | libidn | [] [] [] [] | liferea | [] [] [] [] [] | lilypond | [] | lordsawar | | lprng | [] | lynx | [] [] [] [] | m4 | [] [] [] [] [] | mailfromd | | mailutils | | make | [] [] [] [] | man-db | [] [] | man-db-manpages | [] [] | midi-instruments | [] [] [] [] [] [] [] [] [] | minicom | [] [] [] | mkisofs | [] [] | myserver | [] | nano | [] [] [] [] [] | opcodes | [] [] [] | parted | [] [] [] [] | pies | | popt | [] [] [] [] [] [] [] [] [] [] | procps-ng | | procps-ng-man | | psmisc | [] [] [] [] | pspp | [] [] | pushover | [] | pwdutils | [] | pyspread | | radius | [] | recode | [] [] [] [] [] [] [] | recutils | | rpm | [] | rush | [] | sarg | | sed | [] [] [] [] [] [] [] | sharutils | | shishi | | skribilo | [] | solfege | [] [] | solfege-manual | | spotmachine | | sudo | [] [] [] [] | sudoers | [] [] [] | sysstat | [] [] [] | tar | [] [] [] [] [] [] | texinfo | [] [] [] | texinfo_document | [] [] | tigervnc | | tin | | tin-man | | tracgoogleappsa... | [] [] [] [] | trader | [] [] | util-linux | [] | ve | [] | vice | () () | vmm | | vorbis-tools | [] [] | wastesedge | () | wcd | | wcd-man | | wdiff | [] [] [] | wget | [] [] [] [] | wyslij-po | [] [] [] | xboard | | xdg-user-dirs | [] [] [] [] [] [] [] [] [] [] [] [] [] [] | xkeyboard-config | [] [] [] [] [] | +-------------------------------------------------+ ga gd gl gu he hi hr hu hy ia id is it ja ka kk 36 2 49 4 8 2 54 75 2 6 84 11 89 60 0 3 kn ko ku ky lg lt lv mk ml mn mr ms mt nb ne nl +--------------------------------------------------+ a2ps | [] [] | aegis | [] | anubis | [] [] [] | aspell | [] [] | bash | [] [] | bfd | | binutils | | bison | [] | bison-runtime | [] [] [] [] [] [] | buzztrax | | ccd2cue | | ccide | [] [] | cflow | [] | clisp | [] | coreutils | [] [] | cpio | [] | cppi | | cpplib | [] | cryptsetup | [] | datamash | [] [] | denemo | | dfarc | [] [] | dialog | [] [] [] [] [] [] | dico | | diffutils | [] [] [] | dink | [] | direvent | [] | doodle | [] | dos2unix | [] [] | dos2unix-man | [] | e2fsprogs | [] | enscript | [] | exif | [] [] | fetchmail | [] | findutils | [] [] | flex | [] | freedink | [] [] | fusionforge | | gas | | gawk | [] | gcal | | gcc | | gdbm | | gettext-examples | [] [] [] [] [] [] | gettext-runtime | [] [] | gettext-tools | [] | gip | [] [] [] | gjay | | glunarclock | [] [] | gnubiff | [] | gnubik | [] [] | gnucash | () () () () () () () [] | gnuchess | [] [] | gnulib | [] | gnunet | | gnunet-gtk | | gold | | gphoto2 | [] | gprof | [] [] | gpsdrive | [] | gramadoir | [] | grep | [] [] | grub | [] [] [] | gsasl | [] | gss | | gst-plugins-bad | [] | gst-plugins-base | [] [] [] | gst-plugins-good | [] [] [] [] | gst-plugins-ugly | [] [] [] [] [] | gstreamer | [] [] | gtick | [] | gtkam | [] [] | gtkorphan | [] [] | gtkspell | [] [] [] [] [] [] [] | guix | | guix-packages | | gutenprint | [] | hello | [] [] [] | help2man | [] | help2man-texi | | hylafax | [] | idutils | [] | iso_15924 | () [] [] | iso_3166 | [] [] [] () [] [] [] [] [] [] | iso_3166_2 | () [] | iso_4217 | () [] [] [] | iso_639 | [] [] () [] [] [] [] | iso_639_3 | [] () [] | iso_639_5 | () | jwhois | [] [] | kbd | [] | klavaro | [] [] | latrine | | ld | | leafpad | [] [] [] [] [] | libc | [] [] | libexif | [] | libextractor | [] | libgnutls | [] [] | libgpg-error | [] | libgphoto2 | [] | libgphoto2_port | [] | libgsasl | [] | libiconv | [] [] | libidn | [] | liferea | [] [] [] | lilypond | | lordsawar | | lprng | | lynx | [] | m4 | [] | mailfromd | | mailutils | | make | [] [] | man-db | [] | man-db-manpages | [] | midi-instruments | [] [] [] [] [] [] [] | minicom | [] | mkisofs | [] | myserver | | nano | [] [] [] | opcodes | [] | parted | [] | pies | | popt | [] [] [] [] [] | procps-ng | | procps-ng-man | | psmisc | [] | pspp | [] [] | pushover | | pwdutils | [] | pyspread | | radius | [] | recode | [] [] | recutils | [] | rpm | [] | rush | [] | sarg | | sed | [] [] | sharutils | [] | shishi | | skribilo | | solfege | [] [] | solfege-manual | [] | spotmachine | [] | sudo | [] [] | sudoers | [] [] | sysstat | [] [] | tar | [] [] [] | texinfo | [] | texinfo_document | [] | tigervnc | [] | tin | | tin-man | | tracgoogleappsa... | [] [] [] | trader | [] | util-linux | [] | ve | [] | vice | [] | vmm | [] | vorbis-tools | [] | wastesedge | [] | wcd | [] | wcd-man | [] | wdiff | [] | wget | [] [] | wyslij-po | [] | xboard | [] | xdg-user-dirs | [] [] [] [] [] [] [] [] [] [] [] | xkeyboard-config | [] [] [] | +--------------------------------------------------+ kn ko ku ky lg lt lv mk ml mn mr ms mt nb ne nl 5 11 4 6 0 13 24 3 3 3 4 12 2 40 1 126 nn or os pa pl ps pt pt_BR ro ru rw sk sl sq sr +--------------------------------------------------+ a2ps | [] [] [] [] [] [] [] | aegis | [] [] | anubis | [] [] [] | aspell | [] [] [] [] [] [] [] | bash | [] [] [] [] [] | bfd | [] | binutils | [] [] | bison | [] [] [] | bison-runtime | [] [] [] [] [] [] [] [] | buzztrax | | ccd2cue | [] | ccide | [] [] [] | cflow | [] [] | clisp | [] | coreutils | [] [] [] [] | cpio | [] [] [] | cppi | [] [] [] | cpplib | [] [] [] | cryptsetup | [] [] | datamash | [] [] | denemo | | dfarc | [] [] [] | dialog | [] [] [] [] [] [] [] | dico | [] | diffutils | [] [] | dink | | direvent | [] [] | doodle | [] [] | dos2unix | [] [] [] [] | dos2unix-man | [] [] | e2fsprogs | [] | enscript | [] [] [] [] [] [] | exif | [] [] [] [] [] [] | fetchmail | [] [] [] | findutils | [] [] [] [] [] | flex | [] [] [] [] [] | freedink | [] [] [] [] [] | fusionforge | | gas | | gawk | [] | gcal | | gcc | | gdbm | [] [] [] | gettext-examples | [] [] [] [] [] [] [] [] | gettext-runtime | [] [] [] [] [] [] [] [] [] | gettext-tools | [] [] [] [] [] [] [] | gip | [] [] [] [] [] | gjay | [] | glunarclock | [] [] [] [] [] [] | gnubiff | [] | gnubik | [] [] [] [] | gnucash | () () () () [] | gnuchess | [] [] | gnulib | [] [] [] [] [] | gnunet | | gnunet-gtk | | gold | | gphoto2 | [] [] [] [] [] | gprof | [] [] [] [] | gpsdrive | [] | gramadoir | [] [] | grep | [] [] [] [] [] [] | grub | [] [] [] [] [] | gsasl | [] [] [] | gss | [] [] [] [] | gst-plugins-bad | [] [] [] [] | gst-plugins-base | [] [] [] [] [] [] | gst-plugins-good | [] [] [] [] [] [] [] | gst-plugins-ugly | [] [] [] [] [] [] [] | gstreamer | [] [] [] [] [] [] [] | gtick | [] [] [] [] [] | gtkam | [] [] [] [] [] [] | gtkorphan | [] [] [] [] | gtkspell | [] [] [] [] [] [] [] [] [] | guix | | guix-packages | | gutenprint | [] | hello | [] [] [] [] [] [] | help2man | [] [] [] [] | help2man-texi | [] | hylafax | | idutils | [] [] [] | iso_15924 | [] () [] [] [] [] | iso_3166 | [] [] [] [] () [] [] [] [] [] [] [] [] | iso_3166_2 | [] () [] | iso_4217 | [] [] () [] [] [] [] [] | iso_639 | [] [] [] () [] [] [] [] [] [] | iso_639_3 | [] () | iso_639_5 | () [] | jwhois | [] [] [] [] | kbd | [] [] | klavaro | [] [] [] [] [] | latrine | [] | ld | | leafpad | [] [] [] [] [] [] [] [] [] | libc | [] [] [] | libexif | [] () [] | libextractor | [] | libgnutls | [] | libgpg-error | [] [] [] | libgphoto2 | [] | libgphoto2_port | [] [] [] [] [] | libgsasl | [] [] [] [] | libiconv | [] [] [] [] [] | libidn | [] [] [] | liferea | [] [] [] [] () [] [] | lilypond | | lordsawar | | lprng | [] | lynx | [] [] | m4 | [] [] [] [] [] | mailfromd | [] | mailutils | [] | make | [] [] [] | man-db | [] [] [] | man-db-manpages | [] [] [] | midi-instruments | [] [] [] [] [] [] [] [] | minicom | [] [] [] [] | mkisofs | [] [] [] | myserver | [] [] | nano | [] [] [] [] [] [] | opcodes | | parted | [] [] [] [] [] [] | pies | [] | popt | [] [] [] [] [] [] | procps-ng | [] | procps-ng-man | [] | psmisc | [] [] [] [] | pspp | [] [] | pushover | | pwdutils | [] | pyspread | [] [] | radius | [] [] | recode | [] [] [] [] [] [] [] [] | recutils | [] | rpm | [] | rush | [] [] [] | sarg | [] [] | sed | [] [] [] [] [] [] [] [] | sharutils | [] [] [] | shishi | [] [] | skribilo | | solfege | [] [] [] | solfege-manual | [] [] | spotmachine | [] [] | sudo | [] [] [] [] [] | sudoers | [] [] [] [] | sysstat | [] [] [] [] [] | tar | [] [] [] [] [] | texinfo | [] [] [] | texinfo_document | [] [] | tigervnc | | tin | [] | tin-man | | tracgoogleappsa... | [] [] [] [] | trader | [] | util-linux | [] [] | ve | [] [] [] | vice | | vmm | | vorbis-tools | [] [] [] | wastesedge | | wcd | | wcd-man | | wdiff | [] [] [] [] [] | wget | [] [] [] [] [] | wyslij-po | [] [] [] [] | xboard | [] [] | xdg-user-dirs | [] [] [] [] [] [] [] [] [] [] [] [] [] | xkeyboard-config | [] [] [] [] | +--------------------------------------------------+ nn or os pa pl ps pt pt_BR ro ru rw sk sl sq sr 7 3 1 6 117 1 12 84 33 82 3 37 45 7 98 sv sw ta te tg th tr uk ur vi wa wo zh_CN zh_HK +---------------------------------------------------+ a2ps | [] [] [] [] [] | aegis | [] | anubis | [] [] [] [] | aspell | [] [] [] [] | bash | [] [] [] [] [] | bfd | [] [] | binutils | [] [] [] | bison | [] [] [] [] | bison-runtime | [] [] [] [] [] [] | buzztrax | [] [] [] | ccd2cue | [] [] | ccide | [] [] [] | cflow | [] [] [] [] | clisp | | coreutils | [] [] [] [] | cpio | [] [] [] [] [] | cppi | [] [] [] | cpplib | [] [] [] [] [] | cryptsetup | [] [] [] | datamash | [] [] [] | denemo | | dfarc | [] | dialog | [] [] [] [] [] [] | dico | [] | diffutils | [] [] [] [] [] | dink | | direvent | [] [] | doodle | [] [] | dos2unix | [] [] [] | dos2unix-man | [] [] | e2fsprogs | [] [] [] [] | enscript | [] [] [] [] | exif | [] [] [] [] [] | fetchmail | [] [] [] [] | findutils | [] [] [] [] [] | flex | [] [] [] | freedink | [] [] | fusionforge | | gas | [] | gawk | [] [] | gcal | [] [] | gcc | [] [] | gdbm | [] [] | gettext-examples | [] [] [] [] [] [] | gettext-runtime | [] [] [] [] [] [] | gettext-tools | [] [] [] [] [] | gip | [] [] [] [] | gjay | [] [] | glunarclock | [] [] [] [] | gnubiff | [] [] | gnubik | [] [] [] | gnucash | () () () () [] | gnuchess | [] [] | gnulib | [] [] [] [] | gnunet | | gnunet-gtk | | gold | [] [] | gphoto2 | [] [] [] [] | gprof | [] [] [] [] | gpsdrive | [] [] [] | gramadoir | [] [] [] | grep | [] [] [] [] [] | grub | [] [] [] [] | gsasl | [] [] [] [] | gss | [] [] [] | gst-plugins-bad | [] [] [] [] | gst-plugins-base | [] [] [] [] [] | gst-plugins-good | [] [] [] [] [] | gst-plugins-ugly | [] [] [] [] [] | gstreamer | [] [] [] [] [] | gtick | [] [] [] | gtkam | [] [] [] [] | gtkorphan | [] [] [] | gtkspell | [] [] [] [] [] [] [] [] | guix | [] | guix-packages | | gutenprint | [] [] [] | hello | [] [] [] [] [] | help2man | [] [] [] | help2man-texi | [] | hylafax | [] | idutils | [] [] [] | iso_15924 | [] () [] [] () [] | iso_3166 | [] [] () [] [] () [] [] [] | iso_3166_2 | () [] [] () [] | iso_4217 | [] () [] [] () [] [] | iso_639 | [] [] [] () [] [] () [] [] [] | iso_639_3 | [] () [] [] () | iso_639_5 | () [] () | jwhois | [] [] [] [] | kbd | [] [] [] | klavaro | [] [] [] [] [] [] | latrine | [] [] | ld | [] [] [] [] [] | leafpad | [] [] [] [] [] [] | libc | [] [] [] [] [] | libexif | [] () | libextractor | [] [] | libgnutls | [] [] [] [] | libgpg-error | [] [] [] [] | libgphoto2 | [] [] | libgphoto2_port | [] [] [] [] | libgsasl | [] [] [] [] | libiconv | [] [] [] [] [] | libidn | () [] [] [] | liferea | [] [] [] [] [] | lilypond | [] | lordsawar | | lprng | [] | lynx | [] [] [] [] | m4 | [] [] [] | mailfromd | [] [] | mailutils | [] | make | [] [] [] | man-db | [] [] | man-db-manpages | [] | midi-instruments | [] [] [] [] [] [] | minicom | [] [] | mkisofs | [] [] [] | myserver | [] | nano | [] [] [] [] | opcodes | [] [] | parted | [] [] [] [] [] | pies | [] [] | popt | [] [] [] [] [] [] [] | procps-ng | [] [] | procps-ng-man | [] | psmisc | [] [] [] | pspp | [] [] [] | pushover | [] | pwdutils | [] [] | pyspread | [] | radius | [] [] | recode | [] [] [] [] | recutils | [] [] [] | rpm | [] [] [] [] | rush | [] [] | sarg | | sed | [] [] [] [] [] | sharutils | [] [] [] | shishi | [] | skribilo | | solfege | [] [] [] | solfege-manual | [] | spotmachine | [] [] | sudo | [] [] [] [] | sudoers | [] [] [] | sysstat | [] [] [] [] | tar | [] [] [] [] [] | texinfo | [] [] [] | texinfo_document | [] | tigervnc | [] [] | tin | [] | tin-man | | tracgoogleappsa... | [] [] [] [] [] | trader | [] | util-linux | [] [] [] | ve | [] [] [] [] | vice | () () | vmm | | vorbis-tools | [] [] | wastesedge | | wcd | [] [] | wcd-man | [] | wdiff | [] [] [] | wget | [] [] [] | wyslij-po | [] [] | xboard | [] | xdg-user-dirs | [] [] [] [] [] [] [] [] [] | xkeyboard-config | [] [] [] [] | +---------------------------------------------------+ sv sw ta te tg th tr uk ur vi wa wo zh_CN zh_HK 87 1 4 3 0 14 52 114 1 130 7 1 88 7 zh_TW +-------+ a2ps | | 30 aegis | | 9 anubis | | 19 aspell | | 28 bash | [] | 25 bfd | | 9 binutils | | 12 bison | [] | 18 bison-runtime | [] | 38 buzztrax | | 8 ccd2cue | | 7 ccide | | 16 cflow | | 15 clisp | | 10 coreutils | | 20 cpio | [] | 21 cppi | | 16 cpplib | [] | 19 cryptsetup | | 13 datamash | | 11 denemo | | 3 dfarc | | 16 dialog | [] | 42 dico | | 6 diffutils | | 21 dink | | 9 direvent | | 10 doodle | | 12 dos2unix | [] | 17 dos2unix-man | | 8 e2fsprogs | | 14 enscript | | 21 exif | | 26 fetchmail | | 19 findutils | | 28 flex | [] | 18 freedink | | 23 fusionforge | | 3 gas | | 5 gawk | | 12 gcal | | 7 gcc | | 4 gdbm | | 10 gettext-examples | [] | 40 gettext-runtime | [] | 34 gettext-tools | [] | 24 gip | [] | 28 gjay | | 8 glunarclock | [] | 27 gnubiff | | 9 gnubik | | 18 gnucash | () | 6 gnuchess | | 10 gnulib | | 23 gnunet | | 1 gnunet-gtk | | 1 gold | | 7 gphoto2 | [] | 19 gprof | | 21 gpsdrive | | 13 gramadoir | | 14 grep | [] | 30 grub | | 21 gsasl | [] | 19 gss | | 17 gst-plugins-bad | | 13 gst-plugins-base | | 27 gst-plugins-good | | 32 gst-plugins-ugly | | 34 gstreamer | [] | 31 gtick | | 19 gtkam | | 24 gtkorphan | | 20 gtkspell | [] | 48 guix | | 5 guix-packages | | 0 gutenprint | | 13 hello | [] | 29 help2man | | 18 help2man-texi | | 5 hylafax | | 5 idutils | | 14 iso_15924 | [] | 23 iso_3166 | [] | 58 iso_3166_2 | | 9 iso_4217 | [] | 28 iso_639 | [] | 46 iso_639_3 | | 10 iso_639_5 | | 2 jwhois | [] | 20 kbd | | 16 klavaro | | 29 latrine | | 7 ld | [] | 15 leafpad | [] | 40 libc | [] | 24 libexif | | 9 libextractor | | 5 libgnutls | | 13 libgpg-error | | 15 libgphoto2 | | 9 libgphoto2_port | [] | 19 libgsasl | | 18 libiconv | [] | 29 libidn | | 17 liferea | | 29 lilypond | | 10 lordsawar | | 3 lprng | | 3 lynx | | 19 m4 | [] | 22 mailfromd | | 4 mailutils | | 6 make | | 18 man-db | | 14 man-db-manpages | | 9 midi-instruments | [] | 43 minicom | [] | 17 mkisofs | | 13 myserver | | 9 nano | [] | 29 opcodes | | 11 parted | [] | 21 pies | | 4 popt | [] | 36 procps-ng | | 5 procps-ng-man | | 4 psmisc | [] | 21 pspp | | 13 pushover | | 6 pwdutils | | 8 pyspread | | 5 radius | | 9 recode | | 31 recutils | | 9 rpm | [] | 13 rush | | 10 sarg | | 4 sed | [] | 34 sharutils | | 12 shishi | | 6 skribilo | | 3 solfege | | 19 solfege-manual | | 9 spotmachine | | 9 sudo | | 23 sudoers | | 20 sysstat | | 21 tar | [] | 30 texinfo | | 17 texinfo_document | | 11 tigervnc | | 10 tin | [] | 7 tin-man | | 1 tracgoogleappsa... | [] | 22 trader | | 11 util-linux | | 12 ve | | 14 vice | | 1 vmm | | 3 vorbis-tools | | 13 wastesedge | | 2 wcd | | 7 wcd-man | | 3 wdiff | [] | 22 wget | | 22 wyslij-po | | 14 xboard | | 8 xdg-user-dirs | [] | 68 xkeyboard-config | [] | 27 +-------+ 90 teams zh_TW 170 domains 44 2805 Some counters in the preceding matrix are higher than the number of visible blocks let us expect. This is because a few extra PO files are used for implementing regional variants of languages, or language dialects. For a PO file in the matrix above to be effective, the package to which it applies should also have been internationalized and distributed as such by its maintainer. There might be an observable lag between the mere existence a PO file and its wide availability in a distribution. If June 2010 seems to be old, you may fetch a more recent copy of this `ABOUT-NLS' file on most GNU archive sites. The most up-to-date matrix with full percentage details can be found at `http://translationproject.org/extra/matrix.html'. 1.5 Using `gettext' in new packages =================================== If you are writing a freely available program and want to internationalize it you are welcome to use GNU `gettext' in your package. Of course you have to respect the GNU Library General Public License which covers the use of the GNU `gettext' library. This means in particular that even non-free programs can use `libintl' as a shared library, whereas only free software can use `libintl' as a static library or use modified versions of `libintl'. Once the sources are changed appropriately and the setup can handle the use of `gettext' the only thing missing are the translations. The Free Translation Project is also available for packages which are not developed inside the GNU project. Therefore the information given above applies also for every other Free Software Project. Contact `coordinator@translationproject.org' to make the `.pot' files available to the translation teams. ukwm/configure.ac0000664000175000017500000004072013233551503012754 0ustar fengfengAC_PREREQ(2.62) m4_define([ukwm_major_version], [1]) m4_define([ukwm_minor_version], [1]) m4_define([ukwm_micro_version], [4]) m4_define([ukwm_version], [ukwm_major_version.ukwm_minor_version.ukwm_micro_version]) m4_define([ukwm_plugin_api_version], [3]) m4_define([libukwm_api_version], [1]) AC_INIT([ukwm], [ukwm_version], [http://bugzilla.gnome.org/enter_bug.cgi?product=ukwm]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_SRCDIR(src/core/display.c) AC_CONFIG_HEADERS(config.h) AC_CONFIG_SUBDIRS([cogl clutter]) AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz tar-ustar subdir-objects]) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])],) AM_MAINTAINER_MODE([enable]) AC_GNU_SOURCE UKWM_MAJOR_VERSION=ukwm_major_version UKWM_MINOR_VERSION=ukwm_minor_version UKWM_MICRO_VERSION=ukwm_micro_version UKWM_VERSION=ukwm_version UKWM_PLUGIN_API_VERSION=ukwm_plugin_api_version LIBUKWM_API_VERSION=libukwm_api_version AC_SUBST(UKWM_MAJOR_VERSION) AC_SUBST(UKWM_MINOR_VERSION) AC_SUBST(UKWM_MICRO_VERSION) AC_SUBST(UKWM_PLUGIN_API_VERSION) AC_SUBST(UKWM_VERSION) AC_SUBST(LIBUKWM_API_VERSION) # Make the ukwm versions visible to the cogl and clutter subdirs export LIBUKWM_API_VERSION UKWM_VERSION UKWM_PLUGIN_DIR="$libdir/$PACKAGE/plugins" AC_SUBST(UKWM_PLUGIN_DIR) # Honor aclocal flags AC_SUBST(ACLOCAL_AMFLAGS, "\${ACLOCAL_FLAGS}") GETTEXT_PACKAGE=ukwm AC_SUBST(GETTEXT_PACKAGE) AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE",[Name of default gettext domain]) AM_GNU_GETTEXT_VERSION([0.19.6]) AM_GNU_GETTEXT([external]) LT_PREREQ([2.2.6]) LT_INIT([disable-static]) # Debian / Ubuntu set this flag to 'no' in libtool, causing linking errors # (i.e when linking against ukwm-clutter). Not to explicitly redefine such # deps, we enable this flag for everybody. link_all_deplibs=yes link_all_deplibs_CXX=yes AC_PROG_CC AC_PROG_CC_C_O AC_PROG_INSTALL AC_PROG_SED AC_HEADER_STDC PKG_PROG_PKG_CONFIG([0.21]) # Sets GLIB_GENMARSHAL and GLIB_MKENUMS AM_PATH_GLIB_2_0([2.53.2]) CANBERRA_GTK=libcanberra-gtk3 CANBERRA_GTK_VERSION=0.26 LIBWACOM_VERSION=0.13 UKWM_PC_MODULES=" egl gtk+-3.0 >= 3.19.8 gio-unix-2.0 >= 2.35.1 pango >= 1.2.0 cairo >= 1.10.0 gsettings-desktop-schemas >= 3.21.4 json-glib-1.0 upower-glib >= 0.99.0 gnome-desktop-3.0 xcomposite >= 0.2 xcursor xdamage xext xfixes xi >= 1.6.0 xkbfile xkeyboard-config xkbcommon >= 0.4.3 xkbcommon-x11 xrender x11-xcb xcb-randr xcb-res " GLIB_GSETTINGS AC_ARG_ENABLE(verbose-mode, AC_HELP_STRING([--disable-verbose-mode], [disable ukwm's ability to do verbose logging, for embedded/size-sensitive custom builds]),, enable_verbose_mode=yes) if test x$enable_verbose_mode = xyes; then AC_DEFINE(WITH_VERBOSE_MODE,1,[Build with verbose mode support]) fi AC_ARG_ENABLE(sm, AC_HELP_STRING([--disable-sm], [disable ukwm's session management support, for embedded/size-sensitive custom non-GNOME builds]),, enable_sm=auto) AC_ARG_ENABLE(startup-notification, AC_HELP_STRING([--disable-startup-notification], [disable ukwm's startup notification support, for embedded/size-sensitive custom non-GNOME builds]),, enable_startup_notification=auto) AC_ARG_WITH(libcanberra, AC_HELP_STRING([--without-libcanberra], [disable the use of libcanberra for playing sounds]),, with_libcanberra=auto) AC_ARG_WITH(libwacom, AC_HELP_STRING([--without-libwacom], [disable the use of libwacom for advanced tablet management]),, with_libwacom=auto) AC_ARG_WITH(gudev, AC_HELP_STRING([--without-gudev], [disable the use of gudev for device type detection]),, with_gudev=auto) AC_ARG_WITH([xwayland-path], [AS_HELP_STRING([--with-xwayland-path], [Absolute path for an X Wayland server])], [XWAYLAND_PATH="$withval"], [XWAYLAND_PATH="$bindir/Xwayland"]) AC_ARG_ENABLE(installed_tests, AS_HELP_STRING([--enable-installed-tests], [Install test programs (default: no)]),, [enable_installed_tests=no]) AM_CONDITIONAL(BUILDOPT_INSTALL_TESTS, test x$enable_installed_tests = xyes) ## here we get the flags we'll actually use # Unconditionally use this dir to avoid a circular dep with gnomecc GNOME_KEYBINDINGS_KEYSDIR="${datadir}/gnome-control-center/keybindings" AC_SUBST(GNOME_KEYBINDINGS_KEYSDIR) STARTUP_NOTIFICATION_VERSION=0.7 AC_MSG_CHECKING([Startup notification library >= $STARTUP_NOTIFICATION_VERSION]) if $PKG_CONFIG --atleast-version $STARTUP_NOTIFICATION_VERSION libstartup-notification-1.0; then have_startup_notification=yes else have_startup_notification=no fi AC_MSG_RESULT($have_startup_notification) if test x$enable_startup_notification = xyes; then have_startup_notification=yes echo "startup-notification support forced on" elif test x$enable_startup_notification = xauto; then true else have_startup_notification=no fi if test x$have_startup_notification = xyes; then echo "Building with libstartup-notification" UKWM_PC_MODULES="$UKWM_PC_MODULES libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_VERSION" AC_DEFINE(HAVE_STARTUP_NOTIFICATION, , [Building with startup notification support]) else echo "Building without libstartup-notification" fi have_libcanberra=no AC_MSG_CHECKING([libcanberra-gtk]) if test x$with_libcanberra = xno ; then AC_MSG_RESULT([disabled]) else if $PKG_CONFIG --exists $CANBERRA_GTK '>=' $CANBERRA_GTK_VERSION; then have_libcanberra=yes AC_MSG_RESULT(yes) UKWM_PC_MODULES="$UKWM_PC_MODULES $CANBERRA_GTK" AC_DEFINE([HAVE_LIBCANBERRA], 1, [Building with libcanberra for playing sounds]) else AC_MSG_RESULT(no) if test x$with_libcanberra = xyes ; then AC_MSG_ERROR([libcanberra forced and libcanberra-gtk was not found]) fi fi fi have_libwacom=no AC_MSG_CHECKING([libwacom]) if test x$with_libwacom = xno ; then AC_MSG_RESULT([disabled]) else if $PKG_CONFIG --exists libwacom '>=' $LIBWACOM_VERSION; then have_libwacom=yes AC_MSG_RESULT(yes) UKWM_PC_MODULES="$UKWM_PC_MODULES libwacom" AC_DEFINE([HAVE_LIBWACOM], 1, [Building with libwacom for advanced tablet management]) else AC_MSG_RESULT(no) if test x$with_libwacom = xyes ; then AC_MSG_ERROR([libwacom forced but not found]) fi fi fi have_gudev=no AC_MSG_CHECKING([gudev]) if test x$with_gudev = xno ; then AC_MSG_RESULT([disabled]) else if $PKG_CONFIG --exists "gudev-1.0 >= 232"; then have_gudev=yes AC_MSG_RESULT(yes) UKWM_PC_MODULES="$UKWM_PC_MODULES gudev-1.0 >= 232" AC_DEFINE([HAVE_LIBGUDEV], 1, [Building with gudev for device type detection]) else AC_MSG_RESULT(no) if test x$with_gudev = xyes ; then AC_MSG_ERROR([gudev forced but not found]) fi fi fi AC_ARG_ENABLE(remote-desktop, AS_HELP_STRING([--enable-remote-desktop], [enable support for remote desktop and screen cast]), enable_remote_desktop=yes, enable_remote_desktop=no ) AS_IF([test "$enable_remote_desktop" = "yes"], [ UKWM_PC_MODULES="$UKWM_PC_MODULES libpipewire-0.1 >= 0.1.4" PKG_CHECK_EXISTS([libpipewire-0.1], [ pw_micro=`$PKG_CONFIG --modversion libpipewire-0.1 | cut -d. -f3` AC_DEFINE_UNQUOTED([PIPEWIRE_VERSION_MICRO],[$pw_micro], [Pipewire micro version used]) ]) AC_DEFINE([HAVE_REMOTE_DESKTOP],[1], [Defined if screen cast and remote desktop support is enabled]) ]) AM_CONDITIONAL([HAVE_REMOTE_DESKTOP],[test "$enable_remote_desktop" = "yes"]) INTROSPECTION_VERSION=0.9.5 GOBJECT_INTROSPECTION_CHECK([$INTROSPECTION_VERSION]) if test x$found_introspection != xno; then AC_DEFINE(HAVE_INTROSPECTION, 1, [Define if GObject introspection is available]) UKWM_PC_MODULES="$UKWM_PC_MODULES gobject-introspection-1.0" fi AC_SUBST(XWAYLAND_PATH) PKG_CHECK_MODULES(UKWM, $UKWM_PC_MODULES) UKWM_NATIVE_BACKEND_MODULES="libdrm libsystemd libinput >= 1.4 gudev-1.0 gbm >= 10.3" AC_ARG_ENABLE(native-backend, AS_HELP_STRING([--disable-native-backend], [disable ukwm native (KMS) backend]),, enable_native_backend=auto ) have_native_backend="no" AS_IF([test "$enable_native_backend" = "yes"], [have_native_backend=yes], [test "$enable_native_backend" = "auto"], PKG_CHECK_EXISTS([$UKWM_NATIVE_BACKEND_MODULES], [have_native_backend=yes])) AS_IF([test "$have_native_backend" = "yes"], [ PKG_CHECK_MODULES([UKWM_NATIVE_BACKEND], [$UKWM_NATIVE_BACKEND_MODULES]) AC_DEFINE([HAVE_NATIVE_BACKEND],[1], [Define if you want to enable the native (KMS) backend based on systemd]) ]) AM_CONDITIONAL([HAVE_NATIVE_BACKEND],[test "$have_native_backend" = "yes"]) AC_ARG_ENABLE(egl-device, AS_HELP_STRING([--enable-egl-device], [enable support for EGLDevice on top of KMS]), enable_egl_device=yes, enable_egl_device=no ) AS_IF([test "$enable_egl_device" = "yes"], [ AC_DEFINE([HAVE_EGL_DEVICE],[1], [Defined if EGLDevice support is enabled]) ]) UKWM_WAYLAND_MODULES="wayland-server >= 1.13.0" AC_ARG_ENABLE(wayland, AS_HELP_STRING([--disable-wayland], [disable ukwm on wayland support]),, enable_wayland=auto ) AS_IF([test "$enable_wayland" = "yes"], [have_wayland=yes], [test "$enable_wayland" = "auto"], PKG_CHECK_EXISTS([$UKWM_WAYLAND_MODULES], [have_wayland=yes])) AS_IF([test "$have_wayland" = "yes"], [ PKG_CHECK_MODULES([UKWM_WAYLAND], [$UKWM_WAYLAND_MODULES]) AC_PATH_PROG([WAYLAND_SCANNER],[wayland-scanner],[no]) AS_IF([test $WAYLAND_SCANNER = "no"], [AC_MSG_ERROR([Could not find wayland-scanner in your PATH, required for parsing wayland extension protocols])]) AC_SUBST([WAYLAND_SCANNER]) AC_DEFINE([HAVE_WAYLAND],[1],[Define if you want to enable Wayland support]) PKG_CHECK_MODULES(WAYLAND_PROTOCOLS, [wayland-protocols >= 1.9], [ac_wayland_protocols_pkgdatadir=`$PKG_CONFIG --variable=pkgdatadir wayland-protocols`]) AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, $ac_wayland_protocols_pkgdatadir) ]) AM_CONDITIONAL([HAVE_WAYLAND],[test "$have_wayland" = "yes"]) PKG_CHECK_EXISTS([xi >= 1.6.99.1], AC_DEFINE([HAVE_XI23],[1],[Define if you have support for XInput 2.3 or greater])) AC_PATH_XTRA ALL_X_LIBS="$X_LIBS $X_PRE_LIBS -lX11 $X_EXTRA_LIBS" # Check for Xinerama extension - we only support the "XFree86" style, # and not the older Solaris-only version; recent Solaris supports the # XFree86 style. ukwm_save_cppflags="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $X_CFLAGS" have_xinerama=yes AC_CHECK_LIB(Xinerama, XineramaQueryExtension, [AC_CHECK_HEADER(X11/extensions/Xinerama.h, [X_EXTRA_LIBS="-lXinerama $X_EXTRA_LIBS" if test -z "`echo $ALL_X_LIBS | grep "\-lXext" 2> /dev/null`"; then X_EXTRA_LIBS="-lXext $X_EXTRA_LIBS" fi], have_xinerama=no, [#include ])], have_xinerama=no, -lXext $ALL_X_LIBS) AC_MSG_CHECKING(for Xinerama support) AC_MSG_RESULT($have_xinerama) CPPFLAGS="$ukwm_save_cppflags" if test x$have_xinerama = xno; then AC_MSG_ERROR([Xinerama extension was not found]) fi AC_DEFINE_UNQUOTED([XKB_BASE], ["`$PKG_CONFIG --variable xkb_base xkeyboard-config`"], [XKB base dir]) RANDR_LIBS= found_randr=no AC_CHECK_LIB(Xrandr, XRRUpdateConfiguration, [AC_CHECK_HEADER(X11/extensions/Xrandr.h, RANDR_LIBS=-lXrandr found_randr=yes,, [#include ])], , -lXext $ALL_X_LIBS) if test "x$found_randr" = "xyes"; then AC_DEFINE(HAVE_RANDR, , [Have the Xrandr extension library]) PKG_CHECK_EXISTS([xrandr >= 1.5.0], AC_DEFINE([HAVE_XRANDR15],[1],[Define if you have support for XRandR 1.5 or greater])) fi UKWM_LIBS="$UKWM_LIBS $RANDR_LIBS $X_LIBS $X_PRE_LIBS -lX11 $X_EXTRA_LIBS -lm" found_sm=no case "$UKWM_LIBS" in *-lSM*) found_sm=yes ;; *) AC_CHECK_LIB(SM, SmcSaveYourselfDone, [AC_CHECK_HEADERS(X11/SM/SMlib.h, UKWM_LIBS="-lSM -lICE $UKWM_LIBS" found_sm=yes)], , $UKWM_LIBS) ;; esac if test x$enable_sm = xno; then found_sm=no fi if test x$enable_sm = xyes; then if test "$found_sm" = "no"; then AC_MSG_ERROR([--enable-sm forced and -lSM not found]) exit 1 fi fi if test "$found_sm" = "yes"; then AC_DEFINE(HAVE_SM, , [Building with SM support]) fi AM_CONDITIONAL(HAVE_SM, test "$found_sm" = "yes") AC_PATH_PROG(ZENITY, zenity, no) if test x"$ZENITY" = xno; then AC_MSG_ERROR([zenity not found in your path - needed for dialogs]) fi AC_ARG_ENABLE(debug, [ --enable-debug enable debugging],, enable_debug=no) if test "x$enable_debug" = "xyes"; then CFLAGS="$CFLAGS -g -O" fi AC_CHECK_DECL([GL_EXT_x11_sync_object], [], [AC_MSG_ERROR([GL_EXT_x11_sync_object definition not found, please update your GL headers])], [#include ]) AC_PATH_PROG([CVT],[cvt],[]) #### Warnings (last since -Werror can disturb other tests) # Stay command-line compatible with the gnome-common configure option. Here # minimum/yes/maximum are the same, however. AC_ARG_ENABLE(compile_warnings, AS_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],[Turn on compiler warnings]),, enable_compile_warnings=error) changequote(,)dnl if test "$enable_compile_warnings" != no ; then if test "x$GCC" = "xyes"; then case " $CFLAGS " in *[\ \ ]-Wall[\ \ ]*) ;; *) CFLAGS="$CFLAGS -Wall" ;; esac # case " $CFLAGS " in # *[\ \ ]-Wshadow[\ \ ]*) ;; # *) CFLAGS="$CFLAGS -Wshadow" ;; # esac case " $CFLAGS " in *[\ \ ]-Wchar-subscripts[\ \ ]*) ;; *) CFLAGS="$CFLAGS -Wchar-subscripts" ;; esac case " $CFLAGS " in *[\ \ ]-Wmissing-declarations[\ \ ]*) ;; *) CFLAGS="$CFLAGS -Wmissing-declarations" ;; esac case " $CFLAGS " in *[\ \ ]-Wmissing-prototypes[\ \ ]*) ;; *) CFLAGS="$CFLAGS -Wmissing-prototypes" ;; esac case " $CFLAGS " in *[\ \ ]-Wnested-externs[\ \ ]*) ;; *) CFLAGS="$CFLAGS -Wnested-externs" ;; esac case " $CFLAGS " in *[\ \ ]-Wpointer-arith[\ \ ]*) ;; *) CFLAGS="$CFLAGS -Wpointer-arith" ;; esac case " $CFLAGS " in *[\ \ ]-Wcast-align[\ \ ]*) ;; *) CFLAGS="$CFLAGS -Wcast-align" ;; esac case " $CFLAGS " in *[\ \ ]-Wsign-compare[\ \ ]*) ;; *) CFLAGS="$CFLAGS -Wsign-compare" ;; esac if test "$enable_compile_warnings" = error; then case " $CFLAGS " in *[\ \ ]-Werror[\ \ ]*) ;; *) CFLAGS="$CFLAGS -Werror -Wno-error=deprecated-declarations" ;; esac fi fi fi changequote([,])dnl AC_CONFIG_FILES([ Makefile data/Makefile doc/Makefile doc/man/Makefile src/Makefile src/libukwm-$LIBUKWM_API_VERSION.pc:src/libukwm.pc.in src/compositor/plugins/Makefile src/meta/meta-version.h po/Makefile.in ]) AC_OUTPUT # Check that cogl and clutter have their .pc files regenerated with the correct version COGL_PC_VERSION=$(grep Version: cogl/cogl/ukwm-cogl-$LIBUKWM_API_VERSION.pc|awk '{ print $2; }') AS_IF([test "x$COGL_PC_VERSION" != "x$UKWM_VERSION"], [AC_MSG_ERROR([ukwm-cogl pkg-config file not updated, rerun ./configure])]) CLUTTER_PC_VERSION=$(grep Version: clutter/clutter/ukwm-clutter-$LIBUKWM_API_VERSION.pc|awk '{ print $2; }') AS_IF([test "x$CLUTTER_PC_VERSION" != "x$UKWM_VERSION"], [AC_MSG_ERROR([ukwm-clutter pkg-config file not updated, rerun ./configure])]) if test x$enable_verbose_mode = xno; then echo "*** WARNING WARNING WARNING WARNING WARNING" echo "*** Building without verbose mode" echo "*** This means there's no way to debug ukwm problems." echo "*** Please build normal desktop versions of ukwm" echo "*** with verbose mode enabled so users can use it when they report bugs." fi dnl ========================================================================== echo " ukwm-$VERSION prefix: ${prefix} source code location: ${srcdir} compiler: ${CC} Startup notification: ${have_startup_notification} libcanberra: ${have_libcanberra} libwacom: ${have_libwacom} gudev ${have_gudev} Introspection: ${found_introspection} Session management: ${found_sm} Wayland: ${have_wayland} Native (KMS) backend: ${have_native_backend} EGLDevice: ${enable_egl_device} Remote desktop: ${enable_remote_desktop} " UKWM_MINOR_VERSION=ukwm_minor_version if expr $UKWM_MINOR_VERSION % 2 > /dev/null ; then stable_version=`expr $UKWM_MINOR_VERSION - 1` echo "This is the UNSTABLE branch of ukwm" echo -n "Use 3.$stable_version.x for stable " echo "(gnome-3-$stable_version branch in git)" else echo "This is the stable branch of ukwm" fi ukwm/src/0000775000175000017500000000000013254604522011255 5ustar fengfengukwm/src/Makefile.am0000664000175000017500000005647413220600404013315 0ustar fengfeng# Flag build for parallelism; see https://savannah.gnu.org/patch/?6905 .AUTOPARALLEL: lib_LTLIBRARIES = libukwm-@LIBUKWM_API_VERSION@.la SUBDIRS=compositor/plugins EXTRA_DIST = NULL = AM_CPPFLAGS = \ -DCLUTTER_ENABLE_COMPOSITOR_API \ -DCLUTTER_ENABLE_EXPERIMENTAL_API \ -DCOGL_ENABLE_EXPERIMENTAL_API \ -DCOGL_ENABLE_EXPERIMENTAL_2_0_API \ -DCOGL_ENABLE_UKWM_API \ -DCLUTTER_DISABLE_DEPRECATION_WARNINGS \ -DCOGL_DISABLE_DEPRECATION_WARNINGS \ $(UKWM_CFLAGS) \ $(UKWM_NATIVE_BACKEND_CFLAGS) \ -I$(builddir) \ -I$(srcdir) \ -I$(srcdir)/backends \ -I$(srcdir)/core \ -I$(srcdir)/ui \ -I$(srcdir)/compositor \ -I$(top_srcdir)/cogl \ -I$(top_srcdir)/cogl/cogl \ -I$(top_srcdir)/cogl/cogl/winsys \ -I$(top_builddir)/cogl/cogl \ -I$(top_builddir)/cogl \ -I$(top_srcdir)/clutter \ -I$(top_srcdir)/clutter/clutter \ -I$(top_builddir)/clutter \ -I$(top_builddir)/clutter/clutter \ -DUKWM_LIBEXECDIR=\"$(libexecdir)\" \ -DUKWM_LOCALEDIR=\"$(localedir)\" \ -DUKWM_PKGDATADIR=\"$(pkgdatadir)\" \ -DUKWM_DATADIR=\"$(datadir)\" \ -DG_LOG_DOMAIN=\"ukwm\" \ -DSN_API_NOT_YET_FROZEN=1 \ -DUKWM_PKGLIBDIR=\"$(pkglibdir)\" \ -DUKWM_PLUGIN_DIR=\"$(UKWM_PLUGIN_DIR)\" \ -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \ -DXWAYLAND_PATH=\"$(XWAYLAND_PATH)\" \ $(NULL) ukwm_built_sources = \ $(dbus_idle_built_sources) \ $(dbus_display_config_built_sources) \ $(dbus_login1_built_sources) \ meta/meta-enum-types.h \ meta-enum-types.c \ $(NULL) if HAVE_REMOTE_DESKTOP ukwm_built_sources += \ $(dbus_remote_desktop_built_sources) \ $(dbus_screen_cast_built_sources) \ $(NULL) endif if HAVE_WAYLAND ukwm_built_sources += \ pointer-gestures-unstable-v1-protocol.c \ pointer-gestures-unstable-v1-server-protocol.h \ gtk-shell-protocol.c \ gtk-shell-server-protocol.h \ gtk-primary-selection-protocol.c \ gtk-primary-selection-server-protocol.h \ xdg-shell-unstable-v6-protocol.c \ xdg-shell-unstable-v6-server-protocol.h \ relative-pointer-unstable-v1-protocol.c \ relative-pointer-unstable-v1-server-protocol.h \ pointer-constraints-unstable-v1-protocol.c \ pointer-constraints-unstable-v1-server-protocol.h \ tablet-unstable-v2-protocol.c \ tablet-unstable-v2-server-protocol.h \ xdg-foreign-unstable-v1-protocol.c \ xdg-foreign-unstable-v1-server-protocol.h \ linux-dmabuf-unstable-v1-protocol.c \ linux-dmabuf-unstable-v1-server-protocol.h \ keyboard-shortcuts-inhibit-unstable-v1-protocol.c \ keyboard-shortcuts-inhibit-unstable-v1-server-protocol.h \ $(NULL) endif wayland_protocols = \ wayland/protocol/gtk-shell.xml \ wayland/protocol/gtk-primary-selection.xml \ $(NULL) libukwm_@LIBUKWM_API_VERSION@_la_SOURCES = \ backends/meta-backend.c \ meta/meta-backend.h \ meta/meta-settings.h \ backends/meta-backend-private.h \ backends/meta-barrier.c \ backends/meta-barrier-private.h \ backends/meta-cursor.c \ backends/meta-cursor.h \ backends/meta-cursor-tracker.c \ backends/meta-cursor-tracker-private.h \ backends/meta-cursor-renderer.c \ backends/meta-cursor-renderer.h \ backends/meta-dnd-private.h \ backends/meta-egl.c \ backends/meta-egl.h \ backends/meta-egl-ext.h \ backends/meta-display-config-shared.h \ backends/meta-idle-monitor.c \ backends/meta-idle-monitor-private.h \ backends/meta-idle-monitor-dbus.c \ backends/meta-idle-monitor-dbus.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-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.c \ backends/meta-monitor.h \ backends/meta-monitor-manager.c \ meta/meta-monitor-manager.h \ backends/meta-monitor-manager-private.h \ backends/meta-monitor-manager-dummy.c \ backends/meta-monitor-manager-dummy.h \ backends/meta-orientation-manager.c \ backends/meta-orientation-manager.h \ backends/meta-pointer-constraint.c \ backends/meta-pointer-constraint.h \ backends/meta-settings.c \ backends/meta-settings-private.h \ backends/meta-stage.h \ backends/meta-stage.c \ backends/meta-renderer.c \ backends/meta-renderer.h \ backends/meta-renderer-view.c \ backends/meta-renderer-view.h \ backends/edid-parse.c \ backends/edid.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-cursor-renderer-x11.c \ backends/x11/meta-cursor-renderer-x11.h \ backends/x11/cm/meta-backend-x11-cm.c \ backends/x11/cm/meta-backend-x11-cm.h \ backends/x11/cm/meta-renderer-x11-cm.c \ backends/x11/cm/meta-renderer-x11-cm.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-renderer-x11-nested.c \ backends/x11/nested/meta-renderer-x11-nested.h \ backends/x11/meta-idle-monitor-xsync.c \ backends/x11/meta-idle-monitor-xsync.h \ backends/x11/meta-input-settings-x11.c \ backends/x11/meta-input-settings-x11.h \ backends/x11/meta-monitor-manager-xrandr.c \ backends/x11/meta-monitor-manager-xrandr.h \ backends/x11/meta-renderer-x11.c \ backends/x11/meta-renderer-x11.h \ backends/x11/meta-stage-x11-nested.c \ backends/x11/meta-stage-x11-nested.h \ core/meta-accel-parse.c \ core/meta-accel-parse.h \ meta/barrier.h \ core/bell.c \ core/bell.h \ core/boxes.c \ core/boxes-private.h \ meta/boxes.h \ core/meta-border.c \ core/meta-border.h \ core/meta-fraction.c \ core/meta-fraction.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.c \ compositor/meta-background-private.h \ compositor/meta-background-actor.c \ compositor/meta-background-actor-private.h \ compositor/meta-background-image.c \ compositor/meta-background-group.c \ compositor/meta-cullable.c \ compositor/meta-cullable.h \ compositor/meta-dnd.c \ compositor/meta-dnd-actor.c \ compositor/meta-dnd-actor-private.h \ 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-rectangle.c \ compositor/meta-texture-rectangle.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-group.c \ compositor/meta-window-group.h \ compositor/meta-window-shape.c \ compositor/region-utils.c \ compositor/region-utils.h \ meta/compositor.h \ meta/meta-background.h \ meta/meta-background-actor.h \ meta/meta-background-image.h \ meta/meta-background-group.h \ meta/meta-plugin.h \ meta/meta-shadow-factory.h \ meta/meta-window-actor.h \ meta/meta-window-shape.h \ meta/compositor-ukwm.h \ core/constraints.c \ core/constraints.h \ core/core.c \ core/meta-close-dialog.c \ meta/meta-close-dialog.h \ core/meta-close-dialog-default.c \ core/meta-close-dialog-default-private.h \ core/meta-inhibit-shortcuts-dialog.c \ meta/meta-inhibit-shortcuts-dialog.h \ core/meta-inhibit-shortcuts-dialog-default.c \ core/meta-inhibit-shortcuts-dialog-default-private.h \ core/delete.c \ core/display.c \ core/display-private.h \ meta/display.h \ core/edge-resistance.c \ core/edge-resistance.h \ core/events.c \ core/events.h \ core/errors.c \ meta/errors.h \ core/frame.c \ core/frame.h \ core/meta-gesture-tracker.c \ core/meta-gesture-tracker-private.h \ core/keybindings.c \ core/keybindings-private.h \ core/main-private.h \ core/main.c \ core/place.c \ core/place.h \ core/prefs.c \ meta/prefs.h \ core/screen.c \ core/screen-private.h \ meta/screen.h \ core/startup-notification.c \ core/startup-notification-private.h \ meta/types.h \ core/restart.c \ core/stack.c \ core/stack.h \ core/stack-tracker.c \ core/stack-tracker.h \ core/util.c \ meta/util.h \ core/util-private.h \ core/window.c \ core/window-private.h \ meta/window.h \ core/workspace.c \ core/workspace-private.h \ meta/common.h \ core/core.h \ ui/ui.h \ ui/frames.c \ ui/frames.h \ ui/theme.c \ meta/theme.h \ ui/theme-private.h \ ui/ui.c \ x11/atomnames.h \ x11/events.c \ x11/events.h \ x11/group-private.h \ x11/group-props.c \ x11/group-props.h \ x11/group.c \ meta/group.h \ x11/iconcache.c \ x11/iconcache.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 \ x11/ukwm-Xatomtype.h \ $(NULL) if HAVE_REMOTE_DESKTOP libukwm_@LIBUKWM_API_VERSION@_la_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-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 \ $(NULL) endif if HAVE_WAYLAND libukwm_@LIBUKWM_API_VERSION@_la_SOURCES += \ compositor/meta-surface-actor-wayland.c \ compositor/meta-surface-actor-wayland.h \ wayland/meta-wayland.c \ wayland/meta-wayland.h \ wayland/meta-wayland-private.h \ wayland/meta-xwayland.c \ wayland/meta-xwayland.h \ wayland/meta-xwayland-selection.c \ wayland/meta-xwayland-selection-private.h \ wayland/meta-xwayland-private.h \ wayland/meta-wayland-buffer.c \ wayland/meta-wayland-buffer.h \ wayland/meta-wayland-dma-buf.c \ wayland/meta-wayland-dma-buf.h \ wayland/meta-wayland-region.c \ wayland/meta-wayland-region.h \ wayland/meta-wayland-data-device.c \ wayland/meta-wayland-data-device.h \ wayland/meta-wayland-data-device-private.h \ wayland/meta-wayland-egl-stream.c \ wayland/meta-wayland-egl-stream.h \ wayland/meta-wayland-input-device.c \ wayland/meta-wayland-input-device.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-gesture-pinch.c \ wayland/meta-wayland-pointer-gesture-pinch.h \ wayland/meta-wayland-keyboard.c \ wayland/meta-wayland-keyboard.h \ wayland/meta-wayland-pointer.c \ wayland/meta-wayland-pointer.h \ wayland/meta-wayland-pointer-constraints.c \ wayland/meta-wayland-pointer-constraints.h \ wayland/meta-pointer-lock-wayland.c \ wayland/meta-pointer-lock-wayland.h \ wayland/meta-pointer-confinement-wayland.c \ wayland/meta-pointer-confinement-wayland.h \ wayland/meta-wayland-popup.c \ wayland/meta-wayland-popup.h \ wayland/meta-wayland-seat.c \ wayland/meta-wayland-seat.h \ wayland/meta-wayland-tablet.c \ wayland/meta-wayland-tablet.h \ wayland/meta-wayland-tablet-manager.c \ wayland/meta-wayland-tablet-manager.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-tablet-pad.c \ wayland/meta-wayland-tablet-pad.h \ wayland/meta-wayland-tablet-pad-group.c \ wayland/meta-wayland-tablet-pad-group.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-touch.c \ wayland/meta-wayland-touch.h \ wayland/meta-wayland-surface.c \ wayland/meta-wayland-surface.h \ wayland/meta-wayland-surface-role-cursor.c \ wayland/meta-wayland-surface-role-cursor.h \ wayland/meta-wayland-surface-role-tablet-cursor.c \ wayland/meta-wayland-surface-role-tablet-cursor.h \ wayland/meta-wayland-types.h \ wayland/meta-wayland-versions.h \ wayland/meta-wayland-outputs.c \ wayland/meta-wayland-outputs.h \ wayland/meta-wayland-xdg-foreign.c \ wayland/meta-wayland-xdg-foreign.h \ wayland/meta-window-wayland.c \ wayland/meta-window-wayland.h \ wayland/meta-wayland-xdg-shell.c \ wayland/meta-wayland-xdg-shell.h \ wayland/meta-wayland-wl-shell.c \ wayland/meta-wayland-wl-shell.h \ wayland/meta-wayland-gtk-shell.c \ wayland/meta-wayland-gtk-shell.h \ wayland/meta-wayland-inhibit-shortcuts.c \ wayland/meta-wayland-inhibit-shortcuts.h \ wayland/meta-wayland-inhibit-shortcuts-dialog.c \ wayland/meta-wayland-inhibit-shortcuts-dialog.h \ $(NULL) endif if HAVE_NATIVE_BACKEND libukwm_@LIBUKWM_API_VERSION@_la_SOURCES += \ backends/native/meta-backend-native.c \ backends/native/meta-backend-native.h \ backends/native/meta-backend-native-private.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-cursor-renderer-native.c \ backends/native/meta-cursor-renderer-native.h \ backends/native/meta-default-modes.h \ backends/native/meta-idle-monitor-native.c \ backends/native/meta-idle-monitor-native.h \ backends/native/meta-input-settings-native.c \ backends/native/meta-input-settings-native.h \ backends/native/meta-monitor-manager-kms.c \ backends/native/meta-monitor-manager-kms.h \ backends/native/meta-launcher.c \ backends/native/meta-launcher.h \ backends/native/meta-renderer-native.c \ backends/native/meta-renderer-native.h \ backends/native/meta-stage-native.c \ backends/native/meta-stage-native.h \ backends/native/dbus-utils.c \ backends/native/dbus-utils.h \ $(NULL) endif nodist_libukwm_@LIBUKWM_API_VERSION@_la_SOURCES = $(ukwm_built_sources) libukwm_@LIBUKWM_API_VERSION@_la_LDFLAGS = \ -no-undefined -export-symbols-regex "^(meta|ag)_.*" libukwm_@LIBUKWM_API_VERSION@_la_LIBADD = \ $(UKWM_LIBS) \ $(UKWM_NATIVE_BACKEND_LIBS) \ $(top_builddir)/clutter/clutter/libukwm-clutter-$(LIBUKWM_API_VERSION).la \ $(NULL) # Headers installed for plugins; introspected information will # be extracted into Ukwm-.gir libukwminclude_headers = \ meta/barrier.h \ meta/boxes.h \ meta/common.h \ meta/compositor-ukwm.h \ meta/compositor.h \ meta/display.h \ meta/errors.h \ meta/group.h \ meta/keybindings.h \ meta/main.h \ meta/meta-backend.h \ meta/meta-background.h \ meta/meta-background-actor.h \ meta/meta-background-image.h \ meta/meta-background-group.h \ meta/meta-close-dialog.h \ meta/meta-inhibit-shortcuts-dialog.h \ meta/meta-cursor-tracker.h \ meta/meta-dnd.h \ meta/meta-idle-monitor.h \ meta/meta-plugin.h \ meta/meta-monitor-manager.h \ meta/meta-settings.h \ meta/meta-shaped-texture.h \ meta/meta-shadow-factory.h \ meta/meta-window-actor.h \ meta/meta-window-shape.h \ meta/prefs.h \ meta/screen.h \ meta/theme.h \ meta/types.h \ meta/util.h \ meta/window.h \ meta/workspace.h \ $(NULL) libukwminclude_built_headers = \ meta/meta-version.h \ meta/meta-enum-types.h \ $(NULL) libukwminclude_base_headers = \ $(libukwminclude_headers) \ $(libukwminclude_built_headers) libukwmincludedir = $(includedir)/ukwm/meta libukwminclude_HEADERS = \ $(libukwminclude_headers) nodist_libukwminclude_HEADERS = \ $(libukwminclude_built_headers) bin_PROGRAMS=ukwm noinst_PROGRAMS= ukwm_SOURCES = core/ukwm.c ukwm_LDADD = $(UKWM_LIBS) libukwm-@LIBUKWM_API_VERSION@.la libexec_PROGRAMS = ukwm-restart-helper ukwm_restart_helper_SOURCES = core/restart-helper.c ukwm_restart_helper_LDADD = $(UKWM_LIBS) include Makefile-tests.am if HAVE_INTROSPECTION include $(INTROSPECTION_MAKEFILE) # These files are in package-private directories, even though they may be used # by plugins. If you're writing a plugin, use g-ir-compiler --add-include-path # and g-ir-compiler --includedir. girdir = $(pkglibdir) gir_DATA = Meta-$(LIBUKWM_API_VERSION).gir typelibdir = $(pkglibdir) typelib_DATA = Meta-$(LIBUKWM_API_VERSION).typelib INTROSPECTION_GIRS = Meta-$(LIBUKWM_API_VERSION).gir INTROSPECTION_SCANNER_ARGS = \ --add-include-path=$(top_builddir)/clutter/clutter \ --add-include-path=$(top_builddir)/cogl/cogl \ --add-include-path=$(top_builddir)/cogl/cogl-pango \ $(NULL) INTROSPECTION_COMPILER_ARGS = \ --includedir=$(top_builddir)/clutter/clutter \ --includedir=$(top_builddir)/cogl/cogl \ --includedir=$(top_builddir)/cogl/cogl-pango \ $(NULL) INTROSPECTION_SCANNER_ENV = \ PKG_CONFIG_PATH=$(top_builddir)/clutter/clutter/:$(top_builddir)/cogl/cogl/:$(top_builddir)/cogl/cogl-pango/:$${PKG_CONFIG_PATH} Meta-@LIBUKWM_API_VERSION@.gir: libukwm-$(LIBUKWM_API_VERSION).la Meta_@LIBUKWM_API_VERSION@_gir_VERSION = $(LIBUKWM_API_VERSION) Meta_@LIBUKWM_API_VERSION@_gir_INCLUDES = \ GObject-2.0 \ GDesktopEnums-3.0 \ Gdk-3.0 \ Gtk-3.0 \ Cogl-$(LIBUKWM_API_VERSION) \ Clutter-$(LIBUKWM_API_VERSION) \ xlib-2.0 \ xfixes-4.0 \ $(NULL) Meta_@LIBUKWM_API_VERSION@_gir_EXPORT_PACKAGES = libukwm-$(LIBUKWM_API_VERSION) Meta_@LIBUKWM_API_VERSION@_gir_CFLAGS = $(AM_CPPFLAGS) Meta_@LIBUKWM_API_VERSION@_gir_LIBS = libukwm-$(LIBUKWM_API_VERSION).la Meta_@LIBUKWM_API_VERSION@_gir_FILES = \ $(libukwminclude_base_headers) \ $(filter %.c,$(libukwm_@LIBUKWM_API_VERSION@_la_SOURCES) $(nodist_libukwm_@LIBUKWM_API_VERSION@_la_SOURCES)) Meta_@LIBUKWM_API_VERSION@_SCANNERFLAGS = --warn-all --warn-error endif dbus_idle_built_sources = meta-dbus-idle-monitor.c meta-dbus-idle-monitor.h CLEANFILES = \ $(ukwm_built_sources) \ $(typelib_DATA) \ $(gir_DATA) DISTCLEANFILES = \ $(libukwminclude_built_headers) \ $(pkgconfig_DATA) pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libukwm-$(LIBUKWM_API_VERSION).pc EXTRA_DIST += \ $(wayland_protocols) \ libukwm.pc.in \ meta-enum-types.h.in \ meta-enum-types.c.in \ org.freedesktop.login1.xml \ org.ukui.ukwm.DisplayConfig.xml \ org.ukui.ukwm.IdleMonitor.xml \ org.ukui.ukwm.RemoteDesktop.xml \ org.ukui.ukwm.ScreenCast.xml \ backends/native/gen-default-modes.py \ $(NULL) BUILT_SOURCES = \ $(ukwm_built_sources) \ $(libukwminclude_built_headers) UKWM_STAMP_FILES = stamp-meta-enum-types.h CLEANFILES += $(UKWM_STAMP_FILES) meta/meta-enum-types.h: stamp-meta-enum-types.h Makefile @true stamp-meta-enum-types.h: $(libukwminclude_headers) meta-enum-types.h.in $(AM_V_GEN) ( cd $(srcdir) && \ $(GLIB_MKENUMS) \ --template meta-enum-types.h.in \ $(libukwminclude_headers) ) > xgen-teth && \ (cmp -s xgen-teth meta/meta-enum-types.h || cp xgen-teth meta/meta-enum-types.h) && \ rm -f xgen-teth && \ echo timestamp > $(@F) meta-enum-types.c: stamp-meta-enum-types.h meta-enum-types.c.in $(AM_V_GEN) ( cd $(srcdir) && \ $(GLIB_MKENUMS) \ --template meta-enum-types.c.in \ $(libukwminclude_headers) ) > xgen-tetc && \ cp xgen-tetc meta-enum-types.c && \ rm -f xgen-tetc dbus_display_config_built_sources = meta-dbus-display-config.c meta-dbus-display-config.h $(dbus_display_config_built_sources) : Makefile.am org.ukui.ukwm.DisplayConfig.xml $(AM_V_GEN)gdbus-codegen \ --interface-prefix org.ukui.ukwm \ --c-namespace MetaDBus \ --generate-c-code meta-dbus-display-config \ --c-generate-autocleanup all \ $(srcdir)/org.ukui.ukwm.DisplayConfig.xml $(dbus_idle_built_sources) : Makefile.am org.ukui.ukwm.IdleMonitor.xml $(AM_V_GEN)gdbus-codegen \ --interface-prefix org.ukui.ukwm \ --c-namespace MetaDBus \ --generate-c-code meta-dbus-idle-monitor \ --c-generate-object-manager \ --c-generate-autocleanup all \ $(srcdir)/org.ukui.ukwm.IdleMonitor.xml if HAVE_REMOTE_DESKTOP dbus_remote_desktop_built_sources = meta-dbus-remote-desktop.c meta-dbus-remote-desktop.h $(dbus_remote_desktop_built_sources) : Makefile.am org.ukui.ukwm.RemoteDesktop.xml $(AM_V_GEN)gdbus-codegen \ --interface-prefix org.ukui.ukwm \ --c-namespace MetaDBus \ --generate-c-code meta-dbus-remote-desktop \ $(srcdir)/org.ukui.ukwm.RemoteDesktop.xml dbus_screen_cast_built_sources = meta-dbus-screen-cast.c meta-dbus-screen-cast.h $(dbus_screen_cast_built_sources) : Makefile.am org.ukui.ukwm.ScreenCast.xml $(AM_V_GEN)gdbus-codegen \ --interface-prefix org.ukui.ukwm \ --c-namespace MetaDBus \ --generate-c-code meta-dbus-screen-cast \ $(srcdir)/org.ukui.ukwm.ScreenCast.xml endif dbus_login1_built_sources = meta-dbus-login1.c meta-dbus-login1.h $(dbus_login1_built_sources) : Makefile.am org.freedesktop.login1.xml $(AM_V_GEN)gdbus-codegen \ --interface-prefix org.freedesktop.login1 \ --c-namespace Login1 \ --generate-c-code meta-dbus-login1 \ --c-generate-autocleanup all \ $(srcdir)/org.freedesktop.login1.xml backends/native/meta-default-modes.h: backends/native/gen-default-modes.py Makefile.am @if test -n "$(CVT)"; then \ if $(AM_V_P); then PS4= set -x; else echo " GEN $@"; fi; \ python $< > $@; \ fi .SECONDEXPANSION: define protostability $(shell echo $1 | sed 's/.*\(\\|\\).*/\1/') endef define protoname $(shell echo $1 | sed 's/\([a-z\-]\+\)-[a-z]\+-v[0-9]\+/\1/') endef %-protocol.c : $(WAYLAND_PROTOCOLS_DATADIR)/$$(call protostability,$$*)/$$(call protoname,$$*)/$$*.xml $(AM_V_GEN)$(WAYLAND_SCANNER) code $< $@ %-server-protocol.h : $(WAYLAND_PROTOCOLS_DATADIR)/$$(call protostability,$$*)/$$(call protoname,$$*)/$$*.xml $(AM_V_GEN)$(WAYLAND_SCANNER) server-header $< $@ %-protocol.c : $(srcdir)/wayland/protocol/%.xml $(AM_V_GEN)$(WAYLAND_SCANNER) code $< $@ %-server-protocol.h : $(srcdir)/wayland/protocol/%.xml $(AM_V_GEN)$(WAYLAND_SCANNER) server-header $< $@ keyboard-shortcuts-inhibit-unstable-v1-protocol.c : $(WAYLAND_PROTOCOLS_DATADIR)/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml $(AM_V_GEN)$(WAYLAND_SCANNER) code < $< > $@ keyboard-shortcuts-inhibit-unstable-v1-server-protocol.h : $(WAYLAND_PROTOCOLS_DATADIR)/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml $(AM_V_GEN)$(WAYLAND_SCANNER) server-header < $< > $@ ukwm/src/org.ukui.ukwm.RemoteDesktop.xml0000664000175000017500000000447613220600404017307 0ustar fengfeng ukwm/src/core/0000775000175000017500000000000013260055411012177 5ustar fengfengukwm/src/core/stack.c0000664000175000017500000011131613220600404013445 0ustar fengfeng/* -*- 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 #include "stack.h" #include "window-private.h" #include #include "frame.h" #include #include #include #include "backends/meta-logical-monitor.h" #include #include "x11/group-private.h" #define WINDOW_HAS_TRANSIENT_TYPE(w) \ (w->type == META_WINDOW_DIALOG || \ w->type == META_WINDOW_MODAL_DIALOG || \ w->type == META_WINDOW_TOOLBAR || \ w->type == META_WINDOW_MENU || \ w->type == META_WINDOW_UTILITY) #define WINDOW_TRANSIENT_FOR_WHOLE_GROUP(w) \ (WINDOW_HAS_TRANSIENT_TYPE (w) && w->transient_for == NULL) #define WINDOW_IN_STACK(w) (w->stack_position >= 0) static void stack_sync_to_xserver (MetaStack *stack); static void meta_window_set_stack_position_no_sync (MetaWindow *window, int position); static void stack_do_window_deletions (MetaStack *stack); static void stack_do_window_additions (MetaStack *stack); 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); MetaStack* meta_stack_new (MetaScreen *screen) { MetaStack *stack; stack = g_new (MetaStack, 1); stack->screen = screen; stack->xwindows = g_array_new (FALSE, FALSE, sizeof (Window)); stack->sorted = NULL; stack->added = NULL; stack->removed = NULL; stack->freeze_count = 0; stack->n_positions = 0; stack->need_resort = FALSE; stack->need_relayer = FALSE; stack->need_constrain = FALSE; return stack; } void meta_stack_free (MetaStack *stack) { g_array_free (stack->xwindows, TRUE); g_list_free (stack->sorted); g_list_free (stack->added); g_list_free (stack->removed); g_free (stack); } void meta_stack_add (MetaStack *stack, MetaWindow *window) { g_return_if_fail (!window->override_redirect); meta_topic (META_DEBUG_STACK, "Adding window %s to the stack\n", window->desc); if (window->stack_position >= 0) meta_bug ("Window %s had stack position already\n", window->desc); stack->added = g_list_prepend (stack->added, 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); stack_sync_to_xserver (stack); meta_stack_update_window_tile_matches (stack, window->screen->active_workspace); } void meta_stack_remove (MetaStack *stack, MetaWindow *window) { meta_topic (META_DEBUG_STACK, "Removing window %s from the stack\n", window->desc); if (window->stack_position < 0) meta_bug ("Window %s removed from stack but had no stack position\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; /* We don't know if it's been moved from "added" to "stack" yet */ stack->added = g_list_remove (stack->added, window); stack->sorted = g_list_remove (stack->sorted, window); /* stack->removed is only used to update stack->xwindows */ if (window->client_type == META_WINDOW_CLIENT_TYPE_X11) { /* Remember the window ID to remove it from the stack array. * The macro is safe to use: Window is guaranteed to be 32 bits, and * GUINT_TO_POINTER says it only works on 32 bits. */ stack->removed = g_list_prepend (stack->removed, GUINT_TO_POINTER (window->xwindow)); if (window->frame) stack->removed = g_list_prepend (stack->removed, GUINT_TO_POINTER (window->frame->xwindow)); } stack_sync_to_xserver (stack); meta_stack_update_window_tile_matches (stack, window->screen->active_workspace); } void meta_stack_update_layer (MetaStack *stack, MetaWindow *window) { stack->need_relayer = TRUE; stack_sync_to_xserver (stack); meta_stack_update_window_tile_matches (stack, window->screen->active_workspace); } void meta_stack_update_transient (MetaStack *stack, MetaWindow *window) { stack->need_constrain = TRUE; stack_sync_to_xserver (stack); meta_stack_update_window_tile_matches (stack, window->screen->active_workspace); } /* raise/lower within a layer */ void meta_stack_raise (MetaStack *stack, MetaWindow *window) { 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); stack_sync_to_xserver (stack); meta_stack_update_window_tile_matches (stack, window->screen->active_workspace); } void meta_stack_lower (MetaStack *stack, MetaWindow *window) { 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); stack_sync_to_xserver (stack); meta_stack_update_window_tile_matches (stack, window->screen->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; stack_sync_to_xserver (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_match ((MetaWindow *) tmp->data); tmp = tmp->next; } g_list_free (windows); } /* 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: if (window->wm_state_below) layer = META_LAYER_BOTTOM; else if (window->wm_state_above && !META_WINDOW_MAXIMIZED (window)) layer = META_LAYER_TOP; else layer = META_LAYER_NORMAL; 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 void compute_layer (MetaWindow *window) { window->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 (window->layer != META_LAYER_DESKTOP && 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 > window->layer) { meta_topic (META_DEBUG_STACK, "Promoting window %s from layer %u to %u due to group membership\n", window->desc, window->layer, group_max); window->layer = group_max; } } meta_topic (META_DEBUG_STACK, "Window %s on layer %u type = %u has_focus = %d\n", window->desc, window->layer, window->type, window->has_focus); } /* 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; g_assert (above->screen == below->screen); /* 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 (!WINDOW_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 (!WINDOW_IN_STACK (group_window) || w->screen != group_window->screen || 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 (!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 && WINDOW_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) { if (WINDOW_HAS_TRANSIENT_TYPE(above) && 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_window_deletions: * * Go through "deleted" and take the matching windows * out of "windows". */ static void stack_do_window_deletions (MetaStack *stack) { /* Do removals before adds, with paranoid idea that we might re-add * the same window IDs. */ GList *tmp; int i; tmp = 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 = 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 (stack->xwindows, Window, i)) { g_array_remove_index (stack->xwindows, i); goto next; } } next: tmp = tmp->next; } g_list_free (stack->removed); stack->removed = NULL; } static void stack_do_window_additions (MetaStack *stack) { GList *tmp; gint n_added; n_added = g_list_length (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 */ stack->added = g_list_reverse (stack->added); tmp = stack->added; while (tmp != NULL) { MetaWindow *w; w = tmp->data; if (w->client_type == META_WINDOW_CLIENT_TYPE_X11) g_array_append_val (stack->xwindows, w->xwindow); /* add to the main list */ stack->sorted = g_list_prepend (stack->sorted, w); tmp = tmp->next; } stack->need_resort = TRUE; /* may not be needed as we add to top */ stack->need_constrain = TRUE; stack->need_relayer = TRUE; } g_list_free (stack->added); stack->added = NULL; } /** * 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; compute_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_screen_queue_check_fullscreen (stack->screen); 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_window_deletions (stack); stack_do_window_additions (stack); stack_do_relayer (stack); stack_do_constrain (stack); stack_do_resort (stack); } /** * 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 stack_sync_to_xserver (MetaStack *stack) { GArray *x11_stacked; GArray *all_root_children_stacked; /* wayland OR x11 */ GList *tmp; GArray *hidden_stack_ids; /* Bail out if frozen */ if (stack->freeze_count > 0) return; meta_topic (META_DEBUG_STACK, "Syncing window stack to server\n"); stack_ensure_sorted (stack); /* Create stacked xwindow arrays, in bottom-to-top order */ x11_stacked = g_array_new (FALSE, FALSE, sizeof (Window)); all_root_children_stacked = g_array_new (FALSE, FALSE, sizeof (guint64)); hidden_stack_ids = g_array_new (FALSE, FALSE, sizeof (guint64)); meta_topic (META_DEBUG_STACK, "Bottom to top: "); meta_push_no_msg_prefix (); for (tmp = g_list_last(stack->sorted); tmp != NULL; tmp = tmp->prev) { MetaWindow *w = tmp->data; guint64 top_level_window; guint64 stack_id; if (w->unmanaging) continue; meta_topic (META_DEBUG_STACK, "%u:%d - %s ", w->layer, w->stack_position, w->desc); if (w->client_type == META_WINDOW_CLIENT_TYPE_X11) g_array_append_val (x11_stacked, w->xwindow); 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 (); /* The screen guard window sits above all hidden windows and acts as * a barrier to input reaching these windows. */ guint64 guard_window_id = stack->screen->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 (stack->screen->stack_tracker, (guint64 *)all_root_children_stacked->data, all_root_children_stacked->len); meta_stack_tracker_restack_at_bottom (stack->screen->stack_tracker, (guint64 *)hidden_stack_ids->data, hidden_stack_ids->len); /* Sync _NET_CLIENT_LIST and _NET_CLIENT_LIST_STACKING */ XChangeProperty (stack->screen->display->xdisplay, stack->screen->xroot, stack->screen->display->atom__NET_CLIENT_LIST, XA_WINDOW, 32, PropModeReplace, (unsigned char *)stack->xwindows->data, stack->xwindows->len); XChangeProperty (stack->screen->display->xdisplay, stack->screen->xroot, stack->screen->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_array_free (hidden_stack_ids, TRUE); g_array_free (all_root_children_stacked, TRUE); } 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 POINT_IN_RECT (root_x, root_y, rect); } 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->unmaps_pending > 0) continue; if (window->unmanaging) continue; if (!(window->input || window->take_focus)) continue; if (!meta_window_should_be_showing (window)) continue; if (must_be_at_point && !window_contains_point (window, root_x, root_y)) continue; if (window->type == META_WINDOW_DOCK) 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; } int meta_stack_windows_cmp (MetaStack *stack, MetaWindow *window_a, MetaWindow *window_b) { g_return_val_if_fail (window_a->screen == window_b->screen, 0); /* -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"); stack_sync_to_xserver (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->screen->stack != NULL); g_return_if_fail (window->stack_position >= 0); g_return_if_fail (position >= 0); g_return_if_fail (position < window->screen->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->screen->stack->need_resort = TRUE; window->screen->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->screen->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) { meta_window_set_stack_position_no_sync (window, position); stack_sync_to_xserver (window->screen->stack); meta_stack_update_window_tile_matches (window->screen->stack, window->screen->active_workspace); } ukwm/src/core/stack-tracker.h0000664000175000017500000000731613220600404015107 0ustar fengfeng/* -*- 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 #include typedef struct _MetaStackTracker MetaStackTracker; MetaStackTracker *meta_stack_tracker_new (MetaScreen *screen); 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); 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 */ ukwm/src/core/meta-close-dialog-default.c0000664000175000017500000001674013220600404017255 0ustar fengfeng/* -*- 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 * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . * * Author: Carlos Garnacho */ #define _XOPEN_SOURCE /* for kill() */ #include #include "util-private.h" #include "window-private.h" #include #include "meta-close-dialog-default-private.h" #include #include #include 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 * ukwm-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, "ukwm-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->screen->screen_name, _("_Force Quit"), _("_Wait"), "face-sad-symbolic", 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); if (dialog_default->child_watch_id) { g_source_remove (dialog_default->child_watch_id); dialog_default->child_watch_id = 0; } 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); if (dialog->child_watch_id) g_source_remove (dialog->child_watch_id); 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); } ukwm/src/core/testboxes.c0000664000175000017500000014174013220600404014364 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm box operation testing program */ /* * Copyright (C) 2005 Elijah Newren * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include "boxes-private.h" #include #include #include #include /* Just for the definition of the various gravities */ #include /* To initialize random seed */ #include #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); printf ("%s passed.\n", G_STRFUNC); } 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)); printf ("%s passed.\n", G_STRFUNC); } 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)); printf ("%s passed.\n", G_STRFUNC); } 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)); printf ("%s passed.\n", G_STRFUNC); } 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)); printf ("%s passed.\n", G_STRFUNC); } static void free_strut_list (GSList *struts) { GSList *tmp = struts; while (tmp) { g_free (tmp->data); tmp = tmp->next; } g_slist_free (struts); } 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 */ /*************************************************************/ printf ("The next test intentionally causes a warning, " "but it can be ignored.\n"); region = get_screen_region (5); verify_lists_are_equal (region, NULL); /* FIXME: Still to do: * - Create random struts and check the regions somehow */ printf ("%s passed.\n", G_STRFUNC); } 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); printf ("%s passed.\n", G_STRFUNC); } 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); printf ("The next test intentionally causes a warning, " "but it can be ignored.\n"); rect = meta_rect (50, 50, 10000, 10000); min_size.width = 600; min_size.height = 1170; meta_rectangle_clamp_to_fit_into_region (region, fixed_directions, &rect, &min_size); 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); printf ("The next test intentionally causes a warning, " "but it can be ignored.\n"); rect = meta_rect (300, 70, 999999, 999999); min_size.width = 100; min_size.height = 200; fixed_directions = FIXED_DIRECTION_Y; meta_rectangle_clamp_to_fit_into_region (region, fixed_directions, &rect, &min_size); g_assert (rect.width == 100 && rect.height == 999999); meta_rectangle_free_list_and_elements (region); printf ("%s passed.\n", G_STRFUNC); } 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); printf ("%s passed.\n", G_STRFUNC); } 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); printf ("%s passed.\n", G_STRFUNC); } 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); printf ("%s passed.\n", G_STRFUNC); } 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); printf ("%s passed.\n", G_STRFUNC); } 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, NorthWestGravity, 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, NorthGravity, 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, NorthEastGravity, 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, SouthWestGravity, 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, SouthGravity, 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, SouthEastGravity, 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, WestGravity, 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, CenterGravity, 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, EastGravity, 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, StaticGravity, 430, 211); g_assert (meta_rectangle_equal (&rect, &temp)); printf ("%s passed.\n", G_STRFUNC); } #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); printf ("%s passed.\n", G_STRFUNC); } int main(void) { init_random_ness (); test_area (); test_intersect (); test_equal (); test_overlap_funcs (); test_basic_fitting (); test_regions_okay (); test_region_fitting (); test_clamping_to_region (); test_clipping_to_region (); test_shoving_into_region (); /* And now the functions dealing with edges more than boxes */ test_find_onscreen_edges (); test_find_nonintersected_monitor_edges (); /* And now the misfit functions that don't quite fit in anywhere else... */ test_gravity_resize (); test_find_closest_point_to_line (); printf ("All tests passed.\n"); return 0; } ukwm/src/core/meta-fraction.h0000664000175000017500000000170413220600404015075 0ustar fengfeng/* -*- 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 */ ukwm/src/core/keybindings-private.h0000664000175000017500000001122613220600404016322 0ustar fengfeng/* -*- 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 #include "meta-accel-parse.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; gboolean overlay_key_only_pressed; 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; } MetaKeyBindingManager; void meta_display_init_keys (MetaDisplay *display); void meta_display_shutdown_keys (MetaDisplay *display); void meta_screen_grab_keys (MetaScreen *screen); void meta_screen_ungrab_keys (MetaScreen *screen); 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); ClutterModifierType meta_display_get_window_grab_modifiers (MetaDisplay *display); gboolean meta_prefs_add_keybinding (const char *name, GSettings *settings, 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); const char *meta_prefs_get_iso_next_group_option (void); #endif ukwm/src/core/errors.c0000664000175000017500000000401413220600404013650 0ustar fengfeng/* -*- 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. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ /** * SECTION:errors * @title: Errors * @short_description: Ukwm X error handling */ #include #include #include "display-private.h" #include #include #include /* 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_error_trap_push (MetaDisplay *display) { gdk_error_trap_push (); } void meta_error_trap_pop (MetaDisplay *display) { gdk_error_trap_pop_ignored (); } int meta_error_trap_pop_with_return (MetaDisplay *display) { return gdk_error_trap_pop (); } ukwm/src/core/events.c0000664000175000017500000003432113220600404013644 0ustar fengfeng/* -*- 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 "events.h" #include #include "display-private.h" #include "window-private.h" #include "backends/x11/meta-backend-x11.h" #include "backends/meta-cursor-tracker-private.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #include "backends/native/meta-idle-monitor-native.h" #endif #ifdef HAVE_WAYLAND #include "wayland/meta-wayland-private.h" #endif #include "meta-surface-actor.h" #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) 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; /* 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); if (META_IS_SURFACE_ACTOR (source)) return meta_surface_actor_get_window (META_SURFACE_ACTOR (source)); 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 (); } } static void handle_idletime_for_event (const ClutterEvent *event) { #ifdef HAVE_NATIVE_BACKEND /* This is handled by XSync under X11. */ MetaBackend *backend = meta_get_backend (); if (META_IS_BACKEND_NATIVE (backend)) { ClutterInputDevice *device, *source_device; MetaIdleMonitor *core_monitor, *device_monitor; int device_id; device = clutter_event_get_device (event); if (device == 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; device_id = clutter_input_device_get_device_id (device); core_monitor = meta_idle_monitor_get_core (); device_monitor = meta_idle_monitor_get_for_device (device_id); meta_idle_monitor_native_reset_idletime (core_monitor); meta_idle_monitor_native_reset_idletime (device_monitor); source_device = clutter_event_get_source_device (event); if (source_device != device) { device_id = clutter_input_device_get_device_id (device); device_monitor = meta_idle_monitor_get_for_device (device_id); meta_idle_monitor_native_reset_idletime (device_monitor); } } #endif /* HAVE_NATIVE_BACKEND */ } 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 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; ClutterInputDevice *source; 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 (!display->current_pad_osd && (event->type == CLUTTER_PAD_BUTTON_PRESS || event->type == CLUTTER_PAD_BUTTON_RELEASE || event->type == CLUTTER_PAD_RING || event->type == CLUTTER_PAD_STRIP)) { if (meta_input_settings_handle_pad_event (meta_backend_get_input_settings (backend), event)) { bypass_wayland = bypass_clutter = TRUE; goto out; } } source = clutter_event_get_source_device (event); if (source) { meta_backend_update_last_device (backend, clutter_input_device_get_device_id (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 handle_idletime_for_event (event); 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 (CurrentTime == 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 (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); 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. */ if (event->type == CLUTTER_BUTTON_PRESS) { if (META_IS_BACKEND_X11 (backend)) { Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); meta_verbose ("Allowing events time %u\n", (unsigned int)event->button.time); XIAllowEvents (xdisplay, clutter_event_get_device_id (event), XIReplayDevice, event->button.time); } } /* 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; } 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 = CurrentTime; 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; } ukwm/src/core/main.c0000664000175000017500000004275713220600404013300 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm main() */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2006 Elijah Newren * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, 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 Ukwm when it's time to go. * * * * Ukwm - a boring window manager for the adult in you * * Many window managers are like Marshmallow Froot Loops; Ukwm * 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 #include #include "util-private.h" #include "display-private.h" #include #include "ui.h" #include #include #include #include "core/main-private.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_INTROSPECTION #include #endif #include "x11/session.h" #ifdef HAVE_WAYLAND #include "wayland/meta-wayland.h" #include "backends/x11/nested/meta-backend-x11-nested.h" # endif #include "backends/meta-backend-private.h" #include "backends/x11/meta-backend-x11.h" #include "backends/x11/cm/meta-backend-x11-cm.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #ifdef HAVE_WAYLAND #include #endif /* HAVE_WAYLAND */ #endif /* HAVE_NATIVE_BACKEND */ /* * 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 Ukwm * 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 Ukwm. 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_RANDR meta_verbose ("Compiled with randr extension\n"); #else meta_verbose ("Compiled without randr extension\n"); #endif #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 Ukwm. 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 ("Ukwm 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 Ukwm'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; #endif #ifdef HAVE_NATIVE_BACKEND static gboolean opt_display_server; #endif 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 }, #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 {NULL} }; /** * meta_get_option_context: (skip) * * Returns a #GOptionContext initialized with ukwm-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, UKWM_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 Ukwm should use. It first tries to use * @display_name as the display. If @display_name is %NULL then * try to use the environment variable UKWM_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 ("UKWM_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, CurrentTime); /* 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 ("UKWM_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 enviornment 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; #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) run_as_wayland_compositor = check_for_wayland_session_type (); #endif /* HAVE_NATIVE_BACKEND */ 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 ukwm. 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; 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 ("UKWM_VERBOSE")) meta_set_verbose (TRUE); if (g_getenv ("UKWM_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 (UKWM_PKGLIBDIR); #endif #ifdef HAVE_WAYLAND if (meta_is_wayland_compositor ()) meta_wayland_pre_clutter_init (); #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_clutter_init (); #ifdef HAVE_WAYLAND /* Bring up Wayland. This also launches Xwayland and sets DISPLAY as well... */ if (meta_is_wayland_compositor ()) meta_wayland_init (); #endif meta_set_syncing (opt_sync || (g_getenv ("UKWM_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_ui_init (); meta_restart_init (); } /** * meta_register_with_session: * * Registers ukwm 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 ukwm. Call this after completing initialization that doesn't require * an event loop. * * Return value: ukwm'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 Ukwm. 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_retheme_all (); break; default: /* handled elsewhere or otherwise */ break; } } ukwm/src/core/util.c0000664000175000017500000005335413220600404013324 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2005 Elijah Newren * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ /** * SECTION:util * @title: Utility functions * @short_description: Miscellaneous utility functions */ #define _POSIX_C_SOURCE 200112L /* for fdopen() */ #include #include #include "util-private.h" #include #include /* For clutter_threads_add_repaint_func() */ #include #include #include #include #include #include /* must explicitly be included for Solaris; #326746 */ #include /* Just for the definition of the various gravities */ #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 ("UKWM_USE_LOGFILE")) { char *filename = NULL; char *tmpl; int fd; GError *err; tmpl = g_strdup_printf ("ukwm-%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 (_("Ukwm 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; } /** * meta_free_gslist_and_elements: (skip) * @list_to_deep_free: list to deep free * */ void meta_free_gslist_and_elements (GSList *list_to_deep_free) { g_slist_foreach (list_to_deep_free, (void (*)(gpointer,gpointer))&g_free, /* ew, for ugly */ NULL); g_slist_free (list_to_deep_free); } #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_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 (int gravity) { switch (gravity) { case NorthWestGravity: return "NorthWestGravity"; break; case NorthGravity: return "NorthGravity"; break; case NorthEastGravity: return "NorthEastGravity"; break; case WestGravity: return "WestGravity"; break; case CenterGravity: return "CenterGravity"; break; case EastGravity: return "EastGravity"; break; case SouthWestGravity: return "SouthWestGravity"; break; case SouthGravity: return "SouthGravity"; break; case SouthEastGravity: return "SouthEastGravity"; break; case StaticGravity: return "StaticGravity"; break; default: return "NorthWestGravity"; 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) * @type: type of dialog * @message: message * @timeout: timeout * @display: display * @ok_text: text for Ok button * @cancel_text: text for Cancel button * @icon_name: icon name * @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 char *icon_name, 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, "--class"); append_argument (args, "ukwm-dialog"); 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); } if (icon_name) { append_argument (args, "--icon-name"); append_argument (args, icon_name); } 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) { if (later->source) { g_source_remove (later->source); later->source = 0; } later->func = NULL; unref_later (later); } 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 || !later->func (later->data)) 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, "[ukwm] 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, "[ukwm] 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 (); } } 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; } /* eof util.c */ ukwm/src/core/stack.h0000664000175000017500000003474713220600404013466 0ustar fengfeng/* -*- 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 "screen-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 { /** The MetaScreen containing this stack. */ MetaScreen *screen; /** * 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; /** The MetaWindows of the windows we manage, sorted in order. */ GList *sorted; /** * MetaWindows waiting to be added to the "sorted" and "windows" list, after * being added by meta_stack_add() and before being assimilated by * stack_ensure_sorted(). * * 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 * "windows" list, after being removed by meta_stack_remove() and before * being assimilated by stack_ensure_sorted(). (We already removed them * from the "sorted" list.) * * The order of the elements in this list is not important. */ GList *removed; /** * 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; }; /** * meta_stack_new: * @screen: The MetaScreen which will be the parent of this stack. * * Creates and initialises a MetaStack. * * Returns: The new screen. */ MetaStack *meta_stack_new (MetaScreen *screen); /** * meta_stack_free: * @stack: The stack to destroy. * * Destroys and frees a MetaStack. */ void meta_stack_free (MetaStack *stack); /** * 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_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: 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: 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 ukwm/src/core/workspace-private.h0000664000175000017500000000710513220600404016013 0ustar fengfeng/* -*- 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 #include "window-private.h" struct _MetaWorkspace { GObject parent_instance; MetaScreen *screen; 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 (MetaScreen *screen); 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 ukwm/src/core/events.h0000664000175000017500000000206613220600404013652 0ustar fengfeng/* -*- 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 #ifndef META_EVENTS_H #define META_EVENTS_H void meta_display_init_events (MetaDisplay *display); void meta_display_free_events (MetaDisplay *display); #endif ukwm/src/core/meta-accel-parse.h0000664000175000017500000000302313233511035015450 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_ACCEL_PARSE_H #define META_ACCEL_PARSE_H #include #include 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 */ ukwm/src/core/core.c0000664000175000017500000002147713220600404013300 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm 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 * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include #include "core.h" #include "frame.h" #include "workspace-private.h" #include #include #include "util-private.h" #include "x11/window-x11.h" #include "x11/window-x11-private.h" /* Looks up the MetaWindow representing the frame of the given X window. * Used as a helper function by a bunch of the functions below. * * FIXME: The functions that use this function throw the result away * after use. Many of these functions tend to be called in small groups, * which results in get_window() getting called several times in succession * with the same parameters. We should profile to see whether this wastes * much time, and if it does we should look into a generalised * meta_core_get_window_info() which takes a bunch of pointers to variables * to put its results in, and only fills in the non-null ones. */ static MetaWindow * get_window (Display *xdisplay, Window frame_xwindow) { MetaDisplay *display; MetaWindow *window; display = meta_display_for_x_display (xdisplay); window = meta_display_lookup_x_window (display, frame_xwindow); if (window == NULL || window->frame == NULL) { meta_bug ("No such frame window 0x%lx!\n", frame_xwindow); return NULL; } return window; } void meta_core_queue_frame_resize (Display *xdisplay, Window frame_xwindow) { MetaWindow *window = get_window (xdisplay, 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) { 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 (window->screen->active_workspace && meta_window_located_on_workspace (window, window->screen->active_workspace)) { GList* link; link = g_list_find (window->screen->active_workspace->mru_list, window); g_assert (link); window->screen->active_workspace->mru_list = g_list_remove_link (window->screen->active_workspace->mru_list, link); g_list_free (link); window->screen->active_workspace->mru_list = g_list_append (window->screen->active_workspace->mru_list, window); } } return FALSE; } void meta_core_user_lower_and_unfocus (Display *xdisplay, Window frame_xwindow, guint32 timestamp) { MetaWindow *window = get_window (xdisplay, frame_xwindow); 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 (window->screen->active_workspace, NULL, timestamp); } void meta_core_toggle_maximize_vertically (Display *xdisplay, Window frame_xwindow) { MetaWindow *window = get_window (xdisplay, 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_core_toggle_maximize_horizontally (Display *xdisplay, Window frame_xwindow) { MetaWindow *window = get_window (xdisplay, 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_core_toggle_maximize (Display *xdisplay, Window frame_xwindow) { MetaWindow *window = get_window (xdisplay, 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_core_show_window_menu (Display *xdisplay, Window frame_xwindow, MetaWindowMenuType menu, int root_x, int root_y, guint32 timestamp) { MetaWindow *window = get_window (xdisplay, 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_core_show_window_menu_for_rect (Display *xdisplay, Window frame_xwindow, MetaWindowMenuType menu, MetaRectangle *rect, guint32 timestamp) { MetaWindow *window = get_window (xdisplay, 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_core_begin_grab_op (Display *xdisplay, Window frame_xwindow, MetaGrabOp op, gboolean pointer_already_grabbed, gboolean frame_action, int button, gulong modmask, guint32 timestamp, int root_x, int root_y) { MetaWindow *window = get_window (xdisplay, frame_xwindow); MetaDisplay *display; MetaScreen *screen; display = meta_display_for_x_display (xdisplay); screen = display->screen; g_assert (screen != NULL); return meta_display_begin_grab_op (display, screen, window, op, pointer_already_grabbed, frame_action, button, modmask, timestamp, root_x, root_y); } void meta_core_end_grab_op (Display *xdisplay, guint32 timestamp) { MetaDisplay *display; display = meta_display_for_x_display (xdisplay); meta_display_end_grab_op (display, timestamp); } MetaGrabOp meta_core_get_grab_op (Display *xdisplay) { MetaDisplay *display; display = meta_display_for_x_display (xdisplay); return display->grab_op; } void meta_core_grab_buttons (Display *xdisplay, Window frame_xwindow) { MetaDisplay *display; display = meta_display_for_x_display (xdisplay); meta_verbose ("Grabbing buttons on frame 0x%lx\n", frame_xwindow); meta_display_grab_window_buttons (display, frame_xwindow); } void meta_core_set_screen_cursor (Display *xdisplay, Window frame_on_screen, MetaCursor cursor) { MetaWindow *window = get_window (xdisplay, frame_on_screen); meta_frame_set_screen_cursor (window->frame, cursor); } void meta_invalidate_default_icons (void) { /* XXX: Actually invalidate the icons when they're used. */ } void meta_retheme_all (void) { if (meta_get_display ()) meta_display_retheme_all (); } ukwm/src/core/edge-resistance.h0000664000175000017500000000360113220600404015404 0ustar fengfeng/* -*- 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 "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, int gravity, GSourceFunc timeout_func, gboolean snap, gboolean is_keyboard_op); #endif /* META_EDGE_RESISTANCE_H */ ukwm/src/core/workspace.c0000664000175000017500000012654413220600404014347 0ustar fengfeng/* -*- 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 #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "screen-private.h" #include #include "workspace-private.h" #include "boxes-private.h" #include #include #include #include #include #ifdef HAVE_LIBCANBERRA #include #endif void meta_workspace_queue_calc_showing (MetaWorkspace *workspace); static void focus_ancestor_or_top_window (MetaWorkspace *workspace, MetaWindow *not_this_one, guint32 timestamp); static void free_this (gpointer candidate, gpointer dummy); G_DEFINE_TYPE (MetaWorkspace, meta_workspace, G_TYPE_OBJECT); enum { PROP_0, PROP_N_WINDOWS, PROP_WORKSPACE_INDEX, LAST_PROP, }; static GParamSpec *obj_props[LAST_PROP]; 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; 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); } 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, LAST_PROP, obj_props); } static void meta_workspace_init (MetaWorkspace *workspace) { } MetaWorkspace* meta_workspace_new (MetaScreen *screen) { MetaWorkspace *workspace; GSList *windows, *l; workspace = g_object_new (META_TYPE_WORKSPACE, NULL); workspace->screen = screen; workspace->screen->workspaces = g_list_append (workspace->screen->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 (screen->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; } /* Foreach function for workspace_free_struts() */ static void free_this (gpointer candidate, gpointer dummy) { g_free (candidate); } /** * 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_foreach (workspace->all_struts, free_this, NULL); g_slist_free (workspace->all_struts); 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_foreach (workspace->builtin_struts, free_this, NULL); g_slist_free (workspace->builtin_struts); 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) { g_return_if_fail (workspace != workspace->screen->active_workspace); assert_workspace_empty (workspace); workspace->screen->workspaces = g_list_remove (workspace->screen->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) { 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) { 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 void workspace_switch_sound(MetaWorkspace *from, MetaWorkspace *to) { #ifdef HAVE_LIBCANBERRA MetaWorkspaceLayout layout; int i, nw, x, y, fi, ti; const char *e; nw = meta_screen_get_n_workspaces(from->screen); fi = meta_workspace_index(from); ti = meta_workspace_index(to); meta_screen_calc_workspace_layout(from->screen, nw, fi, &layout); for (i = 0; i < nw; i++) if (layout.grid[i] == ti) break; if (i >= nw) { meta_bug("Failed to find destination workspace in layout\n"); goto finish; } y = i / layout.cols; x = i % layout.cols; /* We priorize horizontal over vertical movements here. The rationale for this is that horizontal movements are probably more interesting for sound effects because speakers are usually positioned on a horizontal and not a vertical axis. i.e. your spatial "Woosh!" effects will easily be able to encode horizontal movement but not such much vertical movement. */ if (x < layout.current_col) e = "desktop-switch-left"; else if (x > layout.current_col) e = "desktop-switch-right"; else if (y < layout.current_row) e = "desktop-switch-up"; else if (y > layout.current_row) e = "desktop-switch-down"; else { meta_bug("Uh, origin and destination workspace at same logic position!\n"); goto finish; } ca_context_play(ca_gtk_context_get(), 1, CA_PROP_EVENT_ID, e, CA_PROP_EVENT_DESCRIPTION, "Desktop switched", CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", NULL); finish: meta_screen_free_workspace_layout (&layout); #endif /* HAVE_LIBCANBERRA */ } /** * 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) { MetaWorkspace *old; MetaWindow *move_window; MetaScreen *screen; MetaDisplay *display; 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->screen->active_workspace == workspace) return; /* Free any cached pointers to the workspaces's edges from * a current resize or move operation */ meta_display_cleanup_edges (workspace->screen->display); if (workspace->screen->active_workspace) workspace_switch_sound (workspace->screen->active_workspace, workspace); /* Note that old can be NULL; e.g. when starting up */ old = workspace->screen->active_workspace; workspace->screen->active_workspace = workspace; meta_screen_set_active_workspace_hint (workspace->screen); /* 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 && (old->showing_desktop != workspace->showing_desktop)) meta_screen_update_showing_desktop_hint (workspace->screen); if (old == NULL) return; move_window = NULL; if (meta_grab_op_is_moving (workspace->screen->display->grab_op)) move_window = workspace->screen->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. */ screen = workspace->screen; display = meta_screen_get_display (screen); comp = meta_display_get_compositor (display); direction = 0; current_space = meta_workspace_index (old); new_space = meta_workspace_index (workspace); num_workspaces = meta_screen_get_n_workspaces (workspace->screen); meta_screen_calc_workspace_layout (workspace->screen, num_workspaces, current_space, &layout1); meta_screen_calc_workspace_layout (workspace->screen, num_workspaces, new_space, &layout2); if (meta_get_locale_direction () == META_LOCALE_DIRECTION_RTL) { if (layout1.current_col > layout2.current_col) direction = META_MOTION_RIGHT; else if (layout1.current_col < layout2.current_col) direction = META_MOTION_LEFT; } else { if (layout1.current_col < layout2.current_col) direction = META_MOTION_RIGHT; else if (layout1.current_col > layout2.current_col) direction = META_MOTION_LEFT; } 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_screen_free_workspace_layout (&layout1); meta_screen_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); } /* Emit switched signal from screen.c */ meta_screen_workspace_switched (screen, current_space, new_space, direction); } void meta_workspace_activate (MetaWorkspace *workspace, guint32 timestamp) { meta_workspace_activate_with_focus (workspace, NULL, timestamp); } int meta_workspace_index (MetaWorkspace *workspace) { int ret; ret = g_list_index (workspace->screen->workspaces, workspace); if (ret < 0) meta_bug ("Workspace does not exist to index!\n"); 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->screen->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->screen->active_workspace) meta_display_cleanup_edges (workspace->screen->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_screen_queue_workarea_recalc (workspace->screen); } static MetaStrut * copy_strut(MetaStrut *original) { return g_memdup(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 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); /* 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 ( &workspace->screen->rect, workspace->all_struts); /* STEP 3: Get the work areas (region-to-maximize-to) for the screen and * monitors. */ work_area = workspace->screen->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 = (workspace->screen->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 = (workspace->screen->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 (&workspace->screen->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); MetaScreen *screen = workspace->screen; GSList *l; 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_SCREEN_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_SCREEN_DOWN)) continue; strut->rect.height = screen->rect.height - strut->rect.y; break; case META_SIDE_LEFT: if (meta_monitor_manager_get_logical_monitor_neighbor (monitor_manager, logical_monitor, META_SCREEN_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_SCREEN_RIGHT)) continue; strut->rect.width = screen->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; current_space = meta_workspace_index (workspace); num_workspaces = meta_screen_get_n_workspaces (workspace->screen); meta_screen_calc_workspace_layout (workspace->screen, num_workspaces, current_space, &layout); 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 = 0; if (layout.current_col >= layout.cols) layout.current_col = 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_screen_free_workspace_layout (&layout); return meta_screen_get_workspace_by_index (workspace->screen, 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 == CurrentTime) meta_warning ("CurrentTime used to choose focus window; " "focus window may not be correct.\n"); if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK || !workspace->screen->display->mouse_mode) focus_ancestor_or_top_window (workspace, not_this_one, timestamp); else { MetaWindow * window; window = meta_screen_get_mouse_window (workspace->screen, not_this_one); if (window && window->type != META_WINDOW_DOCK && window->type != META_WINDOW_DESKTOP) { if (timestamp == CurrentTime) { /* We would like for this to never happen. However, if * it does happen then we kludge since using CurrentTime * 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->screen->display->autoraise_window != window && meta_prefs_get_auto_raise ()) { meta_display_queue_autoraise_callback (workspace->screen->display, window); } } else if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_SLOPPY) focus_ancestor_or_top_window (workspace, not_this_one, timestamp); else if (meta_prefs_get_focus_mode () == G_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_focus_the_no_focus_window (workspace->screen->display, workspace->screen, timestamp); } } } static gboolean record_ancestor (MetaWindow *window, void *data) { MetaWindow **result = data; *result = window; return FALSE; /* quit with the first ancestor we find */ } /* 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; ancestor = NULL; meta_window_foreach_ancestor (not_this_one, record_ancestor, &ancestor); if (ancestor != NULL && meta_window_located_on_workspace (ancestor, workspace) && meta_window_showing_on_its_workspace (ancestor)) { meta_topic (META_DEBUG_FOCUS, "Focusing %s, ancestor of %s\n", ancestor->desc, not_this_one->desc); meta_window_focus (ancestor, timestamp); /* Also raise the window if in click-to-focus */ if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK) meta_window_raise (ancestor); return; } } window = meta_stack_get_default_focus_window (workspace->screen->stack, workspace, not_this_one); if (window) { meta_topic (META_DEBUG_FOCUS, "Focusing workspace MRU window %s\n", window->desc); meta_window_focus (window, timestamp); /* Also raise the window if in click-to-focus */ if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK) meta_window_raise (window); } else { meta_topic (META_DEBUG_FOCUS, "No MRU window to focus found; focusing no_focus_window.\n"); meta_display_focus_the_no_focus_window (workspace->screen->display, workspace->screen, timestamp); } } /** * meta_workspace_get_screen: * @workspace: a #MetaWorkspace * * Gets the #MetaScreen that the workspace is part of. * * Return value: (transfer none): the #MetaScreen for the workspace */ MetaScreen * meta_workspace_get_screen (MetaWorkspace *workspace) { return workspace->screen; } ukwm/src/core/frame.h0000664000175000017500000000510413220600404013434 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm X window decorations */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_FRAME_PRIVATE_H #define META_FRAME_PRIVATE_H #include "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 is_flashing : 1; /* used by the visual bell flash */ 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_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 ukwm/src/core/meta-close-dialog-default-private.h0000664000175000017500000000233513220600404020725 0ustar fengfeng/* -*- 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 #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 */ ukwm/src/core/restart.c0000664000175000017500000001511713220600404014026 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat, Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ /* * SECTION:restart * @short_description: Smoothly restart the compositor * * There are some cases where we need to restart Ukwm 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 #include #include #include #include "ui.h" #include "util-private.h" #include "display-private.h" static gboolean restart_helper_started = FALSE; static gboolean restart_message_shown = FALSE; static gboolean is_restart = FALSE; void meta_restart_init (void) { Display *xdisplay = meta_ui_get_display (); Atom atom_restart_helper = XInternAtom (xdisplay, "_UKWM_RESTART_HELPER", False); Window restart_helper_window = None; restart_helper_window = XGetSelectionOwner (xdisplay, atom_restart_helper); if (restart_helper_window) is_restart = TRUE; } 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 Ukwm'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[] = { UKWM_LIBEXECDIR "/ukwm-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; } void meta_restart_finish (void) { if (is_restart) { Display *xdisplay = meta_display_get_xdisplay (meta_get_display ()); Atom atom_restart_helper = XInternAtom (xdisplay, "_UKWM_RESTART_HELPER", False); XSetSelectionOwner (xdisplay, atom_restart_helper, None, CurrentTime); } } /** * meta_is_restart: * * Returns %TRUE if this instance of Ukwm comes from Ukwm * 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; } ukwm/src/core/meta-inhibit-shortcuts-dialog-default-private.h0000664000175000017500000000234013220600404023276 0ustar fengfeng/* * 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 #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 */ ukwm/src/core/meta-close-dialog.c0000664000175000017500000000762413220600404015634 0ustar fengfeng/* -*- 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 "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); } ukwm/src/core/constraints.c0000664000175000017500000020124713220600404014712 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm size/position constraints */ /* * Copyright (C) 2002, 2003 Red Hat, Inc. * Copyright (C) 2003, 2004 Rob Adams * Copyright (C) 2005, 2006 Elijah Newren * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include #include "boxes-private.h" #include "constraints.h" #include "workspace-private.h" #include "place.h" #include #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor-manager-private.h" #include #include #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; 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 */ int 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; } 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, int 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, int resize_gravity, const MetaRectangle *orig, MetaRectangle *new) { 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; /* 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, int 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; 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 = logical_monitor->rect; } else { 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); } cur_workspace = window->screen->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 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; 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->screen->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, MetaRectangle *rect, MetaRectangle *intersection) { MetaPlacementRule flipped_rule = *placement_rule;; MetaRectangle flipped_rect; MetaRectangle flipped_intersection; 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_rect.x, &flipped_rect.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; *intersection = flipped_intersection; } } static gboolean is_custom_rule_satisfied (ConstraintInfo *info, 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 && info->current.width != intersection->width) || (placement_rule->constraint_adjustment & y_constrain_actions && info->current.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; MetaPlacementRule current_rule; if (priority > PRIORITY_CUSTOM_RULE) return TRUE; placement_rule = meta_window_get_placement_rule (window); if (!placement_rule) return TRUE; meta_rectangle_intersect (&info->current, &info->work_area_monitor, &intersection); constraint_satisfied = is_custom_rule_satisfied (info, placement_rule, &intersection); if (constraint_satisfied || check_only) return constraint_satisfied; 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, &info->current, &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, &info->current, &intersection); } meta_rectangle_intersect (&info->current, &info->work_area_monitor, &intersection); constraint_satisfied = is_custom_rule_satisfied (info, placement_rule, &intersection); if (constraint_satisfied) return TRUE; if (current_rule.constraint_adjustment & META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_X) { if (info->current.x != intersection.x) info->current.x = intersection.x; else if (info->current.width != intersection.width) info->current.x -= info->current.width - intersection.width; } if (current_rule.constraint_adjustment & META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_Y) { if (info->current.y != intersection.y) info->current.y = intersection.y; else if (info->current.height != intersection.height) info->current.y -= info->current.height - intersection.height; } meta_rectangle_intersect (&info->current, &info->work_area_monitor, &intersection); constraint_satisfied = is_custom_rule_satisfied (info, placement_rule, &intersection); if (constraint_satisfied) return TRUE; if (current_rule.constraint_adjustment & META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_X) { info->current.x = intersection.x; info->current.width = intersection.width; } if (current_rule.constraint_adjustment & META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_Y) { info->current.y = intersection.y; info->current.height = intersection.height; } 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 (!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) { 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_SIDE_BY_SIDE (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 = window->screen->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_SIDE_BY_SIDE (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_SIDE_BY_SIDE (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; } /* Figure out what original rect to pass to meta_rectangle_resize_with_gravity * See bug 448183 */ if (info->action_type == ACTION_MOVE_AND_RESIZE) start_rect = &info->current; else start_rect = &info->orig; /* 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); /* Figure out what original rect to pass to meta_rectangle_resize_with_gravity * See bug 448183 */ if (info->action_type == ACTION_MOVE_AND_RESIZE) start_rect = &info->current; else start_rect = &info->orig; 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_SIDE_BY_SIDE (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 WestGravity: case NorthGravity: case SouthGravity: case EastGravity: fudge = 2; break; case NorthWestGravity: case SouthWestGravity: case CenterGravity: case NorthEastGravity: case SouthEastGravity: case StaticGravity: 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 WestGravity: case EastGravity: /* Yeah, I suck for doing implicit rounding -- sue me */ new_height = CLAMP (new_height, new_width / maxr, new_width / minr); break; case NorthGravity: case SouthGravity: /* Yeah, I suck for doing implicit rounding -- sue me */ new_width = CLAMP (new_width, new_height * minr, new_height * maxr); break; case NorthWestGravity: case SouthWestGravity: case CenterGravity: case NorthEastGravity: case SouthEastGravity: case StaticGravity: 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; } /* Figure out what original rect to pass to meta_rectangle_resize_with_gravity * See bug 448183 */ if (info->action_type == ACTION_MOVE_AND_RESIZE) start_rect = &info->current; else start_rect = &info->orig; 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) { if (priority > PRIORITY_ENTIRELY_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 || window->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 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; /* 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 || 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.bottom; 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.bottom; 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; } ukwm/src/core/keybindings.c0000664000175000017500000043136513220600404014657 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm Keybindings */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002 Red Hat Inc. * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ /** * SECTION:keybindings * @Title: MetaKeybinding * @Short_Description: Key bindings */ #include #include "keybindings-private.h" #include "workspace-private.h" #include #include #include "edge-resistance.h" #include "frame.h" #include "screen-private.h" #include #include "meta-accel-parse.h" #ifdef __linux__ #include #elif !defined KEY_GRAVE #define KEY_GRAVE 0x29 /* assume the use of xf86-input-keyboard */ #endif #include "backends/meta-monitor-manager-private.h" #include "backends/meta-logical-monitor.h" #include "backends/x11/meta-backend-x11.h" #include "x11/window-x11.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #endif #define SCHEMA_COMMON_KEYBINDINGS "org.gnome.desktop.wm.keybindings" #define SCHEMA_UKWM_KEYBINDINGS "org.ukui.ukwm.keybindings" #define SCHEMA_UKWM_WAYLAND_KEYBINDINGS "org.ukui.ukwm.wayland.keybindings" #define META_KEY_BINDING_PRIMARY_LAYOUT 0 #define META_KEY_BINDING_SECONDARY_LAYOUT 1 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_memdup (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, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event); static gboolean process_keyboard_move_grab (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event); static gboolean process_keyboard_resize_grab (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event); static void grab_key_bindings (MetaDisplay *display); static void ungrab_key_bindings (MetaDisplay *display); 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; }; 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 ukwm 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; 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 ukwm 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 ("Overwriting existing binding of keysym %x" " with keysym %x (keycode %x).\n", 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); 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 = handler->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; } static void ungrab_key_bindings (MetaDisplay *display) { GSList *windows, *l; meta_screen_ungrab_keys (display->screen); 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; meta_screen_grab_keys (display->screen); 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, MetaKeyBindingFlags flags, MetaKeyBindingAction action, MetaKeyHandlerFunc func, int data, gpointer user_data, GDestroyNotify free_data) { MetaKeyHandler *handler; if (!meta_prefs_add_keybinding (name, settings, 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, 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, flags, 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; } static guint get_keybinding_action (MetaKeyBindingManager *keys, MetaResolvedKeyCombo *resolved_combo) { MetaKeyBinding *binding; /* This is much more vague than the MetaDisplay::overlay-key signal, * which is only emitted if the overlay-key is the only key pressed; * as this method is primarily intended for plugins to allow processing * of ukwm keybindings while holding a grab, the overlay-key-only-pressed * tracking is left to the plugin here. */ if (resolved_key_combo_intersect (resolved_combo, &keys->overlay_resolved_key_combo)) return META_KEYBINDING_ACTION_OVERLAY_KEY; 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. */ 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); } } void meta_display_ungrab_window_buttons (MetaDisplay *display, Window xwindow) { MetaKeyBindingManager *keys = &display->key_binding_manager; if (keys->window_grab_modifiers == 0) return; meta_change_buttons_grab (keys, xwindow, FALSE, FALSE, keys->window_grab_modifiers); } 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; } 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_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: { 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); 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; 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 meta_screen_change_keygrabs (MetaScreen *screen, gboolean grab) { MetaDisplay *display = screen->display; MetaKeyBindingManager *keys = &display->key_binding_manager; int i; if (keys->overlay_resolved_key_combo.len != 0) meta_change_keygrab (keys, screen->xroot, grab, &keys->overlay_resolved_key_combo); for (i = 0; i < keys->n_iso_next_group_combos; i++) meta_change_keygrab (keys, screen->xroot, grab, &keys->iso_next_group_combo[i]); change_binding_keygrabs (keys, screen->xroot, FALSE, grab); } void meta_screen_grab_keys (MetaScreen *screen) { if (screen->keys_grabbed) return; meta_screen_change_keygrabs (screen, TRUE); screen->keys_grabbed = TRUE; } void meta_screen_ungrab_keys (MetaScreen *screen) { if (!screen->keys_grabbed) return; meta_screen_change_keygrabs (screen, FALSE); screen->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 (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 (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, MetaScreen *screen, 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) { 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; } meta_change_keygrab (keys, display->screen->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; 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; 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; char *key; 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; meta_change_keygrab (keys, display->screen->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); g_free (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; 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); 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) { 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, MetaScreen *screen, MetaKeyHandler *handler, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding) { if (handler->func) (* handler->func) (display, screen, handler->flags & META_KEY_BINDING_PER_WINDOW ? window : NULL, event, binding, handler->user_data); else (* handler->default_func) (display, screen, handler->flags & META_KEY_BINDING_PER_WINDOW ? window: NULL, event, binding, NULL); } static gboolean process_event (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event) { MetaKeyBindingManager *keys = &display->key_binding_manager; xkb_keycode_t keycode = (xkb_keycode_t) event->hardware_keycode; MetaResolvedKeyCombo resolved_combo = { &keycode, 1 }; MetaKeyBinding *binding; /* we used to have release-based bindings but no longer. */ if (event->type == CLUTTER_KEY_RELEASE) return FALSE; resolved_combo.mask = mask_from_event_params (keys, event->modifier_state); binding = get_keybinding (keys, &resolved_combo); if (!binding || (!window && binding->flags & META_KEY_BINDING_PER_WINDOW)) 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 (binding->handler == NULL) meta_bug ("Binding %s has no handler\n", binding->name); else 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, screen, 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 FALSE; } static gboolean process_overlay_key (MetaDisplay *display, MetaScreen *screen, ClutterKeyEvent *event, MetaWindow *window) { 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 (keys->overlay_key_only_pressed) { if (! resolved_key_combo_has_keycode (&keys->overlay_resolved_key_combo, event->hardware_keycode)) { keys->overlay_key_only_pressed = FALSE; /* OK, the user hit modifier+key rather than pressing and * releasing the ovelay key. We want to handle the key * sequence "normally". Unfortunately, using * XAllowEvents(..., ReplayKeyboard, ...) doesn't quite * work, since global keybindings won't be activated ("this * time, however, the function ignores any passive grabs at * above (toward the root of) the grab_window of the grab * just released.") So, we first explicitly check for one of * our global keybindings, and if not found, we then replay * the event. Other clients with global grabs will be out of * luck. */ if (process_event (display, screen, window, event)) { /* 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); } 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 if (event->type == CLUTTER_KEY_RELEASE) { MetaKeyBinding *binding; keys->overlay_key_only_pressed = FALSE; /* We want to unfreeze events, but keep the grab so that if the user * starts typing into the overlay we get all the keys */ if (xdisplay) XIAllowEvents (xdisplay, clutter_input_device_get_device_id (event->device), XIAsyncDevice, event->time); binding = get_keybinding (keys, &keys->overlay_resolved_key_combo); if (binding && meta_compositor_filter_keybinding (display->compositor, binding)) return TRUE; meta_display_overlay_key_activate (display); } else { /* In some rare race condition, ukwm might not receive the Super_L * KeyRelease event because: * - the compositor might end the modal mode and call XIUngrabDevice * while the key is still down * - passive grabs are only activated on KeyPress and not KeyRelease. * * In this case, keys->overlay_key_only_pressed might be wrong. * Ukwm still ought to acknowledge events, otherwise the X server * will not send the next events. * * https://bugzilla.gnome.org/show_bug.cgi?id=666101 */ if (xdisplay) XIAllowEvents (xdisplay, clutter_input_device_get_device_id (event->device), XIAsyncDevice, event->time); } return TRUE; } else if (event->type == CLUTTER_KEY_PRESS && resolved_key_combo_has_keycode (&keys->overlay_resolved_key_combo, event->hardware_keycode)) { keys->overlay_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 the overlay key */ if (xdisplay) XIAllowEvents (xdisplay, clutter_input_device_get_device_id (event->device), XISyncDevice, event->time); return TRUE; } else return FALSE; } static gboolean process_iso_next_group (MetaDisplay *display, MetaScreen *screen, 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; gboolean all_keys_grabbed; gboolean handled; MetaScreen *screen; /* window may be NULL */ screen = display->screen; all_keys_grabbed = window ? window->all_keys_grabbed : FALSE; if (!all_keys_grabbed) { handled = process_overlay_key (display, screen, event, window); if (handled) return TRUE; handled = process_iso_next_group (display, screen, event); if (handled) 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; 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, screen, window, event); } else { meta_topic (META_DEBUG_KEYBINDINGS, "Processing event for keyboard resize\n"); keep_grab = process_keyboard_resize_grab (display, screen, window, event); } } else { meta_topic (META_DEBUG_KEYBINDINGS, "Processing event for mouse-only move/resize\n"); keep_grab = process_mouse_move_resize_grab (display, screen, window, event); } } if (!keep_grab) meta_display_end_grab_op (display, event->time); return TRUE; } /* Do the normal keybindings */ return process_event (display, screen, window, event); } /* 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: keys->overlay_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 gboolean process_mouse_move_resize_grab (MetaDisplay *display, MetaScreen *screen, 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 (screen->preview_tile_mode != META_TILE_NONE) meta_screen_hide_tile_preview (screen); /* 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->shaken_loose || 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; } return TRUE; } static gboolean process_keyboard_move_grab (MetaDisplay *display, MetaScreen *screen, 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) { /* 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->shaken_loose) meta_window_maximize (window, META_MAXIMIZE_BOTH); 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, MetaScreen *screen, 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, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event) { MetaRectangle frame_rect; gboolean handled; int height_inc; int width_inc; int width, height; gboolean smart_snap; int 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, screen, 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 NorthGravity: case NorthWestGravity: case NorthEastGravity: /* Move bottom edge up */ height -= height_inc; break; case SouthGravity: case SouthWestGravity: case SouthEastGravity: /* Move top edge up */ height += height_inc; break; case EastGravity: case WestGravity: case CenterGravity: g_assert_not_reached (); break; } handled = TRUE; break; case CLUTTER_KEY_Down: case CLUTTER_KEY_KP_Down: switch (gravity) { case NorthGravity: case NorthWestGravity: case NorthEastGravity: /* Move bottom edge down */ height += height_inc; break; case SouthGravity: case SouthWestGravity: case SouthEastGravity: /* Move top edge down */ height -= height_inc; break; case EastGravity: case WestGravity: case CenterGravity: g_assert_not_reached (); break; } handled = TRUE; break; case CLUTTER_KEY_Left: case CLUTTER_KEY_KP_Left: switch (gravity) { case EastGravity: case SouthEastGravity: case NorthEastGravity: /* Move left edge left */ width += width_inc; break; case WestGravity: case SouthWestGravity: case NorthWestGravity: /* Move right edge left */ width -= width_inc; break; case NorthGravity: case SouthGravity: case CenterGravity: g_assert_not_reached (); break; } handled = TRUE; break; case CLUTTER_KEY_Right: case CLUTTER_KEY_KP_Right: switch (gravity) { case EastGravity: case SouthEastGravity: case NorthEastGravity: /* Move left edge right */ width -= width_inc; break; case WestGravity: case SouthWestGravity: case NorthWestGravity: /* Move right edge right */ width += width_inc; break; case NorthGravity: case SouthGravity: case CenterGravity: 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, MetaScreen *screen, MetaWindow *event_window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { gint target = meta_screen_get_n_workspaces(screen) - 1; MetaWorkspace *workspace = meta_screen_get_workspace_by_index (screen, target); meta_workspace_activate (workspace, event->time); } static void handle_switch_to_workspace (MetaDisplay *display, MetaScreen *screen, MetaWindow *event_window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { gint which = binding->handler->data; MetaWorkspace *workspace; if (which < 0) { /* Negative workspace numbers are directions with respect to the * current workspace. */ workspace = meta_workspace_get_neighbor (screen->active_workspace, which); } else { workspace = meta_screen_get_workspace_by_index (screen, 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, MetaScreen *screen, 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, MetaScreen *screen, 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, MetaScreen *screen, 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, MetaScreen *screen, MetaWindow *window, int gravity) { MetaRectangle work_area; MetaRectangle frame_rect; int new_x, new_y; meta_window_get_work_area_all_monitors (window, &work_area); meta_window_get_frame_rect (window, &frame_rect); switch (gravity) { case NorthWestGravity: case WestGravity: case SouthWestGravity: new_x = work_area.x; break; case NorthGravity: case SouthGravity: new_x = frame_rect.x; break; case NorthEastGravity: case EastGravity: case SouthEastGravity: new_x = work_area.x + work_area.width - frame_rect.width; break; default: g_assert_not_reached (); } switch (gravity) { case NorthWestGravity: case NorthGravity: case NorthEastGravity: new_y = work_area.y; break; case WestGravity: case EastGravity: new_y = frame_rect.y; break; case SouthWestGravity: case SouthGravity: case SouthEastGravity: 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, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { handle_move_to_corner_backend (display, screen, window, NorthWestGravity); } static void handle_move_to_corner_ne (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { handle_move_to_corner_backend (display, screen, window, NorthEastGravity); } static void handle_move_to_corner_sw (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { handle_move_to_corner_backend (display, screen, window, SouthWestGravity); } static void handle_move_to_corner_se (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { handle_move_to_corner_backend (display, screen, window, SouthEastGravity); } static void handle_move_to_side_n (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { handle_move_to_corner_backend (display, screen, window, NorthGravity); } static void handle_move_to_side_s (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { handle_move_to_corner_backend (display, screen, window, SouthGravity); } static void handle_move_to_side_e (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { handle_move_to_corner_backend (display, screen, window, EastGravity); } static void handle_move_to_side_w (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { handle_move_to_corner_backend (display, screen, window, WestGravity); } static void handle_move_to_center (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { MetaRectangle work_area; MetaRectangle frame_rect; meta_window_get_work_area_all_monitors (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, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { if (screen->active_workspace->showing_desktop) { meta_screen_unshow_desktop (screen); meta_workspace_focus_default_window (screen->active_workspace, NULL, event->time); } else meta_screen_show_desktop (screen, event->time); } static void handle_panel (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { MetaKeyBindingAction action = binding->handler->data; Atom action_atom; XClientMessageEvent ev; action_atom = None; switch (action) { /* FIXME: The numbers are wrong */ case META_KEYBINDING_ACTION_PANEL_MAIN_MENU: action_atom = display->atom__GNOME_PANEL_ACTION_MAIN_MENU; break; case META_KEYBINDING_ACTION_PANEL_RUN_DIALOG: action_atom = display->atom__GNOME_PANEL_ACTION_RUN_DIALOG; break; default: return; } ev.type = ClientMessage; ev.window = screen->xroot; ev.message_type = 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_error_trap_push (display); /* Release the grab for the panel before sending the event */ XUngrabKeyboard (display->xdisplay, event->time); XSendEvent (display->xdisplay, screen->xroot, False, StructureNotifyMask, (XEvent*) &ev); meta_error_trap_pop (display); } static void handle_activate_window_menu (MetaDisplay *display, MetaScreen *screen, 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, MetaScreen *screen, MetaWindow *event_window, ClutterKeyEvent *event, MetaKeyBinding *binding, gboolean backward) { 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, screen->active_workspace, NULL, backward); if (window) meta_window_activate (window, event->time); } static void handle_switch (MetaDisplay *display, MetaScreen *screen, MetaWindow *event_window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { gboolean backwards = meta_key_binding_is_reversed (binding); do_choose_window (display, screen, event_window, event, binding, backwards); } static void handle_cycle (MetaDisplay *display, MetaScreen *screen, MetaWindow *event_window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { gboolean backwards = meta_key_binding_is_reversed (binding); do_choose_window (display, screen, event_window, event, binding, backwards); } static void handle_toggle_fullscreen (MetaDisplay *display, MetaScreen *screen, 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, MetaScreen *screen, 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 void handle_toggle_tiled (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { MetaTileMode mode = binding->handler->data; if ((META_WINDOW_TILED_LEFT (window) && mode == META_TILE_LEFT) || (META_WINDOW_TILED_RIGHT (window) && mode == META_TILE_RIGHT)) { window->tile_monitor_number = window->saved_maximize ? window->monitor->number : -1; window->tile_mode = window->saved_maximize ? META_TILE_MAXIMIZED : META_TILE_NONE; if (window->saved_maximize) meta_window_maximize (window, META_MAXIMIZE_BOTH); else meta_window_unmaximize (window, META_MAXIMIZE_BOTH); } else if (meta_window_can_tile_side_by_side (window)) { window->tile_monitor_number = window->monitor->number; /* 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. */ window->maximized_horizontally = FALSE; meta_window_tile (window, mode); } } static void handle_toggle_maximized (MetaDisplay *display, MetaScreen *screen, 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, MetaScreen *screen, 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, MetaScreen *screen, 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, MetaScreen *screen, 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, MetaScreen *screen, 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, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { if (window->has_minimize_func) meta_window_minimize (window); } static void handle_begin_move (MetaDisplay *display, MetaScreen *screen, 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, MetaScreen *screen, 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, MetaScreen *screen, 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, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { gint which; MetaWorkspace *workspace; if (window->always_sticky) return; which = meta_screen_get_n_workspaces (screen) - 1; workspace = meta_screen_get_workspace_by_index (screen, which); meta_window_change_workspace (window, workspace); } static void handle_move_to_workspace (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { 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 (screen->active_workspace, which); } else { workspace = meta_screen_get_workspace_by_index (screen, 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->screen->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, MetaScreen *screen, 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, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { /* Get window at pointer */ MetaWindow *above = NULL; /* Check if top */ if (meta_stack_get_top (window->screen->stack) == window) { meta_window_lower (window); return; } /* else check if windows in same layer are intersecting it */ above = meta_stack_get_above (window->screen->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->screen->stack, above, TRUE); } /* window is not obscured */ meta_window_lower (window); } static void handle_raise (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { meta_window_raise (window); } static void handle_lower (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { meta_window_lower (window); } static void handle_set_spew_mark (MetaDisplay *display, MetaScreen *screen, 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, MetaScreen *screen, 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, MetaScreen *screen, 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, MetaScreen *screen, 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_restore_shortcuts (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { ClutterInputDevice *source; 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; } static void init_builtin_key_bindings (MetaDisplay *display) { GSettings *common_keybindings = g_settings_new (SCHEMA_COMMON_KEYBINDINGS); GSettings *ukwm_keybindings = g_settings_new (SCHEMA_UKWM_KEYBINDINGS); GSettings *ukwm_wayland_keybindings = g_settings_new (SCHEMA_UKWM_WAYLAND_KEYBINDINGS); add_builtin_keybinding (display, "switch-to-workspace-1", common_keybindings, META_KEY_BINDING_NONE, 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_KEYBINDING_ACTION_WORKSPACE_2, handle_switch_to_workspace, 1); add_builtin_keybinding (display, "switch-to-workspace-3", common_keybindings, META_KEY_BINDING_NONE, 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_KEYBINDING_ACTION_WORKSPACE_4, handle_switch_to_workspace, 3); add_builtin_keybinding (display, "switch-to-workspace-5", common_keybindings, META_KEY_BINDING_NONE, 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_KEYBINDING_ACTION_WORKSPACE_6, handle_switch_to_workspace, 5); add_builtin_keybinding (display, "switch-to-workspace-7", common_keybindings, META_KEY_BINDING_NONE, 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_KEYBINDING_ACTION_WORKSPACE_8, handle_switch_to_workspace, 7); add_builtin_keybinding (display, "switch-to-workspace-9", common_keybindings, META_KEY_BINDING_NONE, 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_KEYBINDING_ACTION_WORKSPACE_10, handle_switch_to_workspace, 9); add_builtin_keybinding (display, "switch-to-workspace-11", common_keybindings, META_KEY_BINDING_NONE, 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_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); add_builtin_keybinding (display, "switch-to-workspace-last", common_keybindings, META_KEY_BINDING_NONE, META_KEYBINDING_ACTION_WORKSPACE_LAST, handle_switch_to_last_workspace, 0); /* 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); if (access("/usr/bin/ukui-window-switch", 01) != 0) { 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, "switch-monitor", ukwm_keybindings, META_KEY_BINDING_NONE, META_KEYBINDING_ACTION_SWITCH_MONITOR, handle_switch_monitor, 0); add_builtin_keybinding (display, "rotate-monitor", ukwm_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", ukwm_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 1); add_builtin_keybinding (display, "switch-to-session-2", ukwm_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 2); add_builtin_keybinding (display, "switch-to-session-3", ukwm_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 3); add_builtin_keybinding (display, "switch-to-session-4", ukwm_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 4); add_builtin_keybinding (display, "switch-to-session-5", ukwm_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 5); add_builtin_keybinding (display, "switch-to-session-6", ukwm_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 6); add_builtin_keybinding (display, "switch-to-session-7", ukwm_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 7); add_builtin_keybinding (display, "switch-to-session-8", ukwm_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 8); add_builtin_keybinding (display, "switch-to-session-9", ukwm_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 9); add_builtin_keybinding (display, "switch-to-session-10", ukwm_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 10); add_builtin_keybinding (display, "switch-to-session-11", ukwm_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 11); add_builtin_keybinding (display, "switch-to-session-12", ukwm_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", ukwm_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_KEYBINDING_ACTION_ACTIVATE_WINDOW_MENU, handle_activate_window_menu, 0); add_builtin_keybinding (display, "toggle-fullscreen", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_TOGGLE_FULLSCREEN, handle_toggle_fullscreen, 0); add_builtin_keybinding (display, "toggle-maximized", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_TOGGLE_MAXIMIZED, handle_toggle_maximized, 0); add_builtin_keybinding (display, "toggle-tiled-left", ukwm_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_TOGGLE_TILED_LEFT, handle_toggle_tiled, META_TILE_LEFT); add_builtin_keybinding (display, "toggle-tiled-right", ukwm_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_TOGGLE_TILED_RIGHT, handle_toggle_tiled, META_TILE_RIGHT); add_builtin_keybinding (display, "toggle-above", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_TOGGLE_ABOVE, handle_toggle_above, 0); add_builtin_keybinding (display, "maximize", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_MAXIMIZE, handle_maximize, 0); add_builtin_keybinding (display, "unmaximize", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_UNMAXIMIZE, handle_unmaximize, 0); add_builtin_keybinding (display, "toggle-shaded", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_TOGGLE_SHADED, handle_toggle_shaded, 0); add_builtin_keybinding (display, "minimize", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_MINIMIZE, handle_minimize, 0); add_builtin_keybinding (display, "close", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_CLOSE, handle_close, 0); add_builtin_keybinding (display, "begin-move", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_BEGIN_MOVE, handle_begin_move, 0); add_builtin_keybinding (display, "begin-resize", common_keybindings, META_KEY_BINDING_PER_WINDOW, 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_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_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_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_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_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_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_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_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_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_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_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_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_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_12, handle_move_to_workspace, 11); add_builtin_keybinding (display, "move-to-workspace-last", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_LAST, handle_move_to_workspace_last, 0); 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_SCREEN_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_SCREEN_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_SCREEN_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_SCREEN_UP); add_builtin_keybinding (display, "raise-or-lower", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_RAISE_OR_LOWER, handle_raise_or_lower, 0); add_builtin_keybinding (display, "raise", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_RAISE, handle_raise, 0); add_builtin_keybinding (display, "lower", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_LOWER, handle_lower, 0); add_builtin_keybinding (display, "maximize-vertically", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_MAXIMIZE_VERTICALLY, handle_maximize_vertically, 0); add_builtin_keybinding (display, "maximize-horizontally", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_MAXIMIZE_HORIZONTALLY, handle_maximize_horizontally, 0); add_builtin_keybinding (display, "always-on-top", common_keybindings, META_KEY_BINDING_PER_WINDOW, 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_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_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_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_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_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_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_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_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_KEYBINDING_ACTION_MOVE_TO_CENTER, handle_move_to_center, 0); g_object_unref (common_keybindings); g_object_unref (ukwm_keybindings); g_object_unref (ukwm_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; g_hash_table_insert (key_handlers, g_strdup ("overlay-key"), 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 ("iso-next-group"), 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 ("external-grab"), 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); /* 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); } ukwm/src/core/boxes.c0000664000175000017500000020445713220600404013471 0ustar fengfeng/* -*- 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 "boxes-private.h" #include #include /* Just for the definition of the various gravities */ /* 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_memdup (rect, sizeof (MetaRectangle)); } void meta_rectangle_free (MetaRectangle *rect) { g_free (rect); } GType meta_rectangle_get_type (void) { static GType type_id = 0; if (!type_id) type_id = g_boxed_type_register_static (g_intern_static_string ("MetaRectangle"), (GBoxedCopyFunc) meta_rectangle_copy, (GBoxedFreeFunc) meta_rectangle_free); return type_id; } 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, int 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 NorthWestGravity and * StaticGravity the same is because we're not given a location at * which to place the window--the window was already placed * appropriately before. So, NorthWestGravity for this function * means to just leave the upper left corner of the outer window * where it already is, and StaticGravity 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 NorthWestGravity: case WestGravity: case SouthWestGravity: rect->x = old_rect->x; break; case NorthGravity: case CenterGravity: case SouthGravity: /* 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 NorthEastGravity: case EastGravity: case SouthEastGravity: rect->x = old_rect->x + (old_rect->width - new_width); break; case StaticGravity: default: rect->x = old_rect->x; break; } rect->width = new_width; /* Next, the y direction */ switch (gravity) { case NorthWestGravity: case NorthGravity: case NorthEastGravity: rect->y = old_rect->y; break; case WestGravity: case CenterGravity: case EastGravity: /* 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 SouthWestGravity: case SouthGravity: case SouthEastGravity: rect->y = old_rect->y + (old_rect->height - new_height); break; case StaticGravity: 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) { meta_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 (a, b)) { 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_foreach (filled_list, (void (*)(gpointer,gpointer))&g_free, /* ew, for ugly */ NULL); g_list_free (filled_list); } 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; } 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) { meta_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) meta_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) meta_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 (); } } 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_adjecent_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; } ukwm/src/core/startup-notification.c0000664000175000017500000005421213220600404016527 0ustar fengfeng/* -*- 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) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include #include #include #include "display-private.h" #include "screen-private.h" #include "startup-notification-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 15000000 typedef struct _MetaStartupNotificationSequence MetaStartupNotificationSequence; typedef struct _MetaStartupNotificationSequenceClass MetaStartupNotificationSequenceClass; enum { PROP_SN_0, PROP_SN_DISPLAY, N_SN_PROPS }; enum { PROP_SEQ_0, PROP_SEQ_ID, PROP_SEQ_TIMESTAMP, N_SEQ_PROPS }; enum { SN_CHANGED, N_SN_SIGNALS }; static guint sn_signals[N_SN_SIGNALS]; static GParamSpec *sn_props[N_SN_PROPS]; static GParamSpec *seq_props[N_SEQ_PROPS]; typedef struct { GSList *list; gint64 now; } CollectTimedOutData; struct _MetaStartupNotification { GObject parent_instance; MetaDisplay *display; #ifdef HAVE_STARTUP_NOTIFICATION SnDisplay *sn_display; SnMonitorContext *sn_context; #endif GSList *startup_sequences; guint startup_sequence_timeout; }; #define META_TYPE_STARTUP_NOTIFICATION_SEQUENCE \ (meta_startup_notification_sequence_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaStartupNotificationSequence, meta_startup_notification_sequence, META, STARTUP_NOTIFICATION_SEQUENCE, GObject) typedef struct { gchar *id; gint64 timestamp; } MetaStartupNotificationSequencePrivate; struct _MetaStartupNotificationSequenceClass { GObjectClass parent_class; void (* complete) (MetaStartupNotificationSequence *sequence); }; G_DEFINE_TYPE (MetaStartupNotification, meta_startup_notification, G_TYPE_OBJECT) G_DEFINE_TYPE_WITH_PRIVATE (MetaStartupNotificationSequence, meta_startup_notification_sequence, G_TYPE_OBJECT) #ifdef HAVE_STARTUP_NOTIFICATION enum { PROP_SEQ_X11_0, PROP_SEQ_X11_SEQ, N_SEQ_X11_PROPS }; struct _MetaStartupNotificationSequenceX11 { MetaStartupNotificationSequence parent_instance; SnStartupSequence *seq; }; static GParamSpec *seq_x11_props[N_SEQ_X11_PROPS]; #define META_TYPE_STARTUP_NOTIFICATION_SEQUENCE_X11 \ (meta_startup_notification_sequence_x11_get_type ()) G_DECLARE_FINAL_TYPE (MetaStartupNotificationSequenceX11, meta_startup_notification_sequence_x11, META, STARTUP_NOTIFICATION_SEQUENCE_X11, MetaStartupNotificationSequence) G_DEFINE_TYPE (MetaStartupNotificationSequenceX11, meta_startup_notification_sequence_x11, META_TYPE_STARTUP_NOTIFICATION_SEQUENCE) static void meta_startup_notification_ensure_timeout (MetaStartupNotification *sn); #endif static void meta_startup_notification_update_feedback (MetaStartupNotification *sn) { MetaScreen *screen = sn->display->screen; if (sn->startup_sequences != NULL) { meta_topic (META_DEBUG_STARTUP, "Setting busy cursor\n"); meta_screen_set_cursor (screen, META_CURSOR_BUSY); } else { meta_topic (META_DEBUG_STARTUP, "Setting default cursor\n"); meta_screen_set_cursor (screen, META_CURSOR_DEFAULT); } } static void meta_startup_notification_sequence_init (MetaStartupNotificationSequence *seq) { } static void meta_startup_notification_sequence_finalize (GObject *object) { MetaStartupNotificationSequence *seq; MetaStartupNotificationSequencePrivate *priv; seq = META_STARTUP_NOTIFICATION_SEQUENCE (object); priv = meta_startup_notification_sequence_get_instance_private (seq); g_free (priv->id); G_OBJECT_CLASS (meta_startup_notification_sequence_parent_class)->finalize (object); } static void meta_startup_notification_sequence_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaStartupNotificationSequence *seq; MetaStartupNotificationSequencePrivate *priv; seq = META_STARTUP_NOTIFICATION_SEQUENCE (object); priv = meta_startup_notification_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_int64 (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_startup_notification_sequence_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaStartupNotificationSequence *seq; MetaStartupNotificationSequencePrivate *priv; seq = META_STARTUP_NOTIFICATION_SEQUENCE (object); priv = meta_startup_notification_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_int64 (value, priv->timestamp); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_startup_notification_sequence_class_init (MetaStartupNotificationSequenceClass *klass) { GObjectClass *object_class; object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_startup_notification_sequence_finalize; object_class->set_property = meta_startup_notification_sequence_set_property; object_class->get_property = meta_startup_notification_sequence_get_property; 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_int64 ("timestamp", "Timestamp", "Timestamp", G_MININT64, G_MAXINT64, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (object_class, N_SEQ_PROPS, seq_props); } static const gchar * meta_startup_notification_sequence_get_id (MetaStartupNotificationSequence *seq) { MetaStartupNotificationSequencePrivate *priv; priv = meta_startup_notification_sequence_get_instance_private (seq); return priv->id; } #ifdef HAVE_STARTUP_NOTIFICATION static gint64 meta_startup_notification_sequence_get_timestamp (MetaStartupNotificationSequence *seq) { MetaStartupNotificationSequencePrivate *priv; priv = meta_startup_notification_sequence_get_instance_private (seq); return priv->timestamp; } static void meta_startup_notification_sequence_complete (MetaStartupNotificationSequence *seq) { MetaStartupNotificationSequenceClass *klass; klass = META_STARTUP_NOTIFICATION_SEQUENCE_GET_CLASS (seq); if (klass->complete) klass->complete (seq); } #endif #ifdef HAVE_STARTUP_NOTIFICATION static void meta_startup_notification_sequence_x11_complete (MetaStartupNotificationSequence *seq) { MetaStartupNotificationSequenceX11 *seq_x11; seq_x11 = META_STARTUP_NOTIFICATION_SEQUENCE_X11 (seq); sn_startup_sequence_complete (seq_x11->seq); } static void meta_startup_notification_sequence_x11_finalize (GObject *object) { MetaStartupNotificationSequenceX11 *seq; seq = META_STARTUP_NOTIFICATION_SEQUENCE_X11 (object); sn_startup_sequence_unref (seq->seq); G_OBJECT_CLASS (meta_startup_notification_sequence_x11_parent_class)->finalize (object); } static void meta_startup_notification_sequence_x11_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaStartupNotificationSequenceX11 *seq; seq = META_STARTUP_NOTIFICATION_SEQUENCE_X11 (object); switch (prop_id) { case PROP_SEQ_X11_SEQ: seq->seq = g_value_get_pointer (value); sn_startup_sequence_ref (seq->seq); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_startup_notification_sequence_x11_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaStartupNotificationSequenceX11 *seq; seq = META_STARTUP_NOTIFICATION_SEQUENCE_X11 (object); switch (prop_id) { case PROP_SEQ_X11_SEQ: g_value_set_pointer (value, seq->seq); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_startup_notification_sequence_x11_init (MetaStartupNotificationSequenceX11 *seq) { } static void meta_startup_notification_sequence_x11_class_init (MetaStartupNotificationSequenceX11Class *klass) { MetaStartupNotificationSequenceClass *seq_class; GObjectClass *object_class; seq_class = META_STARTUP_NOTIFICATION_SEQUENCE_CLASS (klass); seq_class->complete = meta_startup_notification_sequence_x11_complete; object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_startup_notification_sequence_x11_finalize; object_class->set_property = meta_startup_notification_sequence_x11_set_property; object_class->get_property = meta_startup_notification_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 MetaStartupNotificationSequence * meta_startup_notification_sequence_x11_new (SnStartupSequence *seq) { gint64 timestamp; timestamp = sn_startup_sequence_get_timestamp (seq) * 1000; return g_object_new (META_TYPE_STARTUP_NOTIFICATION_SEQUENCE_X11, "id", sn_startup_sequence_get_id (seq), "timestamp", timestamp, "seq", seq, NULL); } static void meta_startup_notification_add_sequence_internal (MetaStartupNotification *sn, MetaStartupNotificationSequence *seq) { sn->startup_sequences = g_slist_prepend (sn->startup_sequences, g_object_ref (seq)); meta_startup_notification_ensure_timeout (sn); meta_startup_notification_update_feedback (sn); } static void collect_timed_out_foreach (void *element, void *data) { MetaStartupNotificationSequence *sequence = element; CollectTimedOutData *ctod = data; gint64 elapsed, timestamp; timestamp = meta_startup_notification_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, meta_startup_notification_sequence_get_id (sequence)); if (elapsed > STARTUP_TIMEOUT) 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 (); g_slist_foreach (sn->startup_sequences, collect_timed_out_foreach, &ctod); for (l = ctod.list; l != NULL; l = l->next) { MetaStartupNotificationSequence *sequence = l->data; meta_topic (META_DEBUG_STARTUP, "Timed out sequence %s\n", meta_startup_notification_sequence_get_id (sequence)); meta_startup_notification_sequence_complete (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, "[ukwm] startup_sequence_timeout"); } #endif static void meta_startup_notification_remove_sequence_internal (MetaStartupNotification *sn, MetaStartupNotificationSequence *seq) { sn->startup_sequences = g_slist_remove (sn->startup_sequences, seq); meta_startup_notification_update_feedback (sn); if (sn->startup_sequences == NULL && sn->startup_sequence_timeout != 0) { g_source_remove (sn->startup_sequence_timeout); sn->startup_sequence_timeout = 0; } g_object_unref (seq); } static MetaStartupNotificationSequence * meta_startup_notification_lookup_sequence (MetaStartupNotification *sn, const gchar *id) { MetaStartupNotificationSequence *seq; const gchar *seq_id; GSList *l; for (l = sn->startup_sequences; l; l = l->next) { seq = l->data; seq_id = meta_startup_notification_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); #ifdef HAVE_STARTUP_NOTIFICATION sn_monitor_context_unref (sn->sn_context); sn_display_unref (sn->sn_display); #endif if (sn->startup_sequence_timeout) g_source_remove (sn->startup_sequence_timeout); g_slist_foreach (sn->startup_sequences, (GFunc) g_object_unref, NULL); g_slist_free (sn->startup_sequences); 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_SN_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_SN_DISPLAY: g_value_set_object (value, sn->display); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } #ifdef HAVE_STARTUP_NOTIFICATION static void sn_error_trap_push (SnDisplay *sn_display, Display *xdisplay) { MetaDisplay *display; display = meta_display_for_x_display (xdisplay); if (display != NULL) meta_error_trap_push (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_error_trap_pop (display); } static void meta_startup_notification_sn_event (SnMonitorEvent *event, void *user_data) { MetaStartupNotification *sn = user_data; MetaStartupNotificationSequence *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_notification_sequence_x11_new (sequence); meta_startup_notification_add_sequence_internal (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)); meta_startup_notification_remove_sequence (sn, sn_startup_sequence_get_id (sequence)); } 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; } g_signal_emit (sn, sn_signals[SN_CHANGED], 0, sequence); sn_startup_sequence_unref (sequence); } #endif static void meta_startup_notification_constructed (GObject *object) { MetaStartupNotification *sn = META_STARTUP_NOTIFICATION (object); g_assert (sn->display != NULL); #ifdef HAVE_STARTUP_NOTIFICATION sn->sn_display = sn_display_new (sn->display->xdisplay, sn_error_trap_push, sn_error_trap_pop); sn->sn_context = sn_monitor_context_new (sn->sn_display, meta_ui_get_screen_number (), meta_startup_notification_sn_event, sn, NULL); #endif 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_SN_DISPLAY] = g_param_spec_object ("display", "Display", "Display", META_TYPE_DISPLAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); sn_signals[SN_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_SN_PROPS, sn_props); } MetaStartupNotification * meta_startup_notification_get (MetaDisplay *display) { static MetaStartupNotification *notification = NULL; if (!notification) notification = g_object_new (META_TYPE_STARTUP_NOTIFICATION, "display", display, NULL); return notification; } void meta_startup_notification_remove_sequence (MetaStartupNotification *sn, const gchar *id) { MetaStartupNotificationSequence *seq; seq = meta_startup_notification_lookup_sequence (sn, id); if (seq) meta_startup_notification_remove_sequence_internal (sn, seq); } gboolean meta_startup_notification_handle_xevent (MetaStartupNotification *sn, XEvent *xevent) { #ifdef HAVE_STARTUP_NOTIFICATION return sn_display_process_event (sn->sn_display, xevent); #endif return FALSE; } GSList * meta_startup_notification_get_sequences (MetaStartupNotification *sn) { GSList *sequences = NULL; #ifdef HAVE_STARTUP_NOTIFICATION GSList *l; /* We return a list of SnStartupSequences here */ for (l = sn->startup_sequences; l; l = l->next) { MetaStartupNotificationSequenceX11 *seq_x11; if (!META_IS_STARTUP_NOTIFICATION_SEQUENCE_X11 (l->data)) continue; seq_x11 = META_STARTUP_NOTIFICATION_SEQUENCE_X11 (l->data); sequences = g_slist_prepend (sequences, seq_x11->seq); } #endif return sequences; } ukwm/src/core/main-private.h0000664000175000017500000000220513220600404014735 0ustar fengfeng/* -*- 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 typedef enum _MetaCompositorType { #ifdef HAVE_WAYLAND META_COMPOSITOR_TYPE_WAYLAND, #endif META_COMPOSITOR_TYPE_X11, } MetaCompositorType; void meta_override_compositor_configuration (MetaCompositorType compositor_type, GType backend_gtype); #endif /* META_MAIN_PRIVATE_H */ ukwm/src/core/delete.c0000664000175000017500000000640613220600404013605 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm window deletion */ /* * Copyright (C) 2001, 2002 Havoc Pennington * Copyright (C) 2004 Elijah Newren * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #define _XOPEN_SOURCE /* for kill() */ #include #include "util-private.h" #include "window-private.h" #include "compositor-private.h" #include #include #include 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) { g_clear_object (&window->close_dialog); } ukwm/src/core/bell.c0000664000175000017500000002246413220600404013263 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm visual bell */ /* * Copyright (C) 2002 Sun Microsystems Inc. * Copyright (C) 2005, 2006 Elijah Newren * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ /* * SECTION:bell * @short_description: Ring the bell or flash the screen * * Sometimes, X programs "ring the bell", whatever that means. Ukwm 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_frame(), unless there is no such window, in * which case it flashes the screen instead. bell_flash_window_frame() * flashes the frame and calls bell_unflash_frame() as a timeout to * remove the flash. * * 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 #include "bell.h" #include "screen-private.h" #include "window-private.h" #include "util-private.h" #include "compositor/compositor-private.h" #include #include #ifdef HAVE_LIBCANBERRA #include #endif /** * 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_screen (display->compositor, display->screen); } /** * bell_unflash_frame: * @data: The frame to unflash, cast to a gpointer so it can go into * a callback function. * * Makes a frame be not flashed; this is the timeout half of * bell_flash_window_frame(). This is done simply by clearing the * flash flag and queuing a redraw of the frame. * * If the configure script found we had no XKB, this does not exist. * * Returns: Always FALSE, so we don't get called again. */ /* * Bug: This is the parallel to bell_flash_window_frame(), so it should * really be called meta_bell_unflash_window_frame(). */ static gboolean bell_unflash_frame (gpointer data) { MetaFrame *frame = (MetaFrame *) data; frame->is_flashing = 0; meta_frame_queue_draw (frame); return FALSE; } /** * bell_flash_window_frame: * @window: The window to flash * * Makes a frame flash and then return to normal shortly afterwards. * This is done by setting a flag so that the theme * code will temporarily draw the frame as focussed if it's unfocussed and * vice versa, and then queueing a redraw. Lastly, we create a timeout so * that the flag can be unset and the frame re-redrawn. * * If the configure script found we had no XKB, this does not exist. */ static void bell_flash_window_frame (MetaWindow *window) { guint id; g_assert (window->frame != NULL); window->frame->is_flashing = 1; meta_frame_queue_draw (window->frame); /* Since this idle is added after the Clutter clock source, with * the same priority, it will be executed after it as well, so * we are guaranteed to get at least one frame drawn in the * flashed state, no matter how loaded we are. */ id = g_timeout_add_full (META_PRIORITY_REDRAW, 100, bell_unflash_frame, window->frame, NULL); g_source_set_name_by_id (id, "[ukwm] bell_unflash_frame"); } 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 && window->frame) bell_flash_window_frame (window); else 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 G_DESKTOP_VISUAL_BELL_FULLSCREEN_FLASH: bell_flash_fullscreen (display); break; case G_DESKTOP_VISUAL_BELL_FRAME_FLASH: bell_flash_frame (display, window); break; } } static gboolean bell_audible_notify (MetaDisplay *display, MetaWindow *window) { #ifdef HAVE_LIBCANBERRA ca_proplist *p; int res; ca_proplist_create (&p); ca_proplist_sets (p, CA_PROP_EVENT_ID, "bell-window-system"); ca_proplist_sets (p, CA_PROP_EVENT_DESCRIPTION, _("Bell event")); ca_proplist_sets (p, CA_PROP_CANBERRA_CACHE_CONTROL, "permanent"); if (window) { ca_proplist_sets (p, CA_PROP_WINDOW_NAME, window->title); ca_proplist_setf (p, CA_PROP_WINDOW_X11_XID, "%lu", (unsigned long)window->xwindow); ca_proplist_sets (p, CA_PROP_APPLICATION_NAME, window->res_name); ca_proplist_setf (p, CA_PROP_APPLICATION_PROCESS_ID, "%d", window->net_wm_pid); } res = ca_context_play_full (ca_gtk_context_get (), 1, p, NULL, NULL); ca_proplist_destroy (p); return res == CA_SUCCESS || res == CA_ERROR_DISABLED; #endif /* HAVE_LIBCANBERRA */ return FALSE; } 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; } void meta_bell_set_audible (MetaDisplay *display, gboolean audible) { #ifdef HAVE_LIBCANBERRA /* When we are playing sounds using libcanberra support, we handle the * bell whether its an audible bell or a visible bell */ gboolean enable_system_bell = FALSE; #else gboolean enable_system_bell = audible; #endif /* HAVE_LIBCANBERRA */ XkbChangeEnabledControls (display->xdisplay, XkbUseCoreKbd, XkbAudibleBellMask, enable_system_bell ? XkbAudibleBellMask : 0); } gboolean meta_bell_init (MetaDisplay *display) { int xkb_base_error_type, xkb_opcode; if (!XkbQueryExtension (display->xdisplay, &xkb_opcode, &display->xkb_base_event_type, &xkb_base_error_type, NULL, NULL)) { display->xkb_base_event_type = -1; g_message ("could not find XKB extension."); return FALSE; } else { unsigned int mask = XkbBellNotifyMask; gboolean visual_bell_auto_reset = FALSE; /* TRUE if and when non-broken version is available */ XkbSelectEvents (display->xdisplay, XkbUseCoreKbd, XkbBellNotifyMask, XkbBellNotifyMask); meta_bell_set_audible (display, meta_prefs_bell_is_audible ()); if (visual_bell_auto_reset) { XkbSetAutoResetControls (display->xdisplay, XkbAudibleBellMask, &mask, &mask); } return TRUE; } return FALSE; } void meta_bell_shutdown (MetaDisplay *display) { /* TODO: persist initial bell state in display, reset here */ XkbChangeEnabledControls (display->xdisplay, XkbUseCoreKbd, XkbAudibleBellMask, XkbAudibleBellMask); } /** * meta_bell_notify_frame_destroy: * @frame: The frame which is being destroyed * * Deals with a frame being destroyed. This is important because if we're * using a visual bell, we might be flashing the edges of the frame, and * so we'd have a timeout function waiting ready to un-flash them. If the * frame's going away, we can tell the timeout not to bother. */ void meta_bell_notify_frame_destroy (MetaFrame *frame) { if (frame->is_flashing) g_source_remove_by_funcs_user_data (&g_timeout_funcs, frame); } ukwm/src/core/core.h0000664000175000017500000000707713220600404013305 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm interface used by GTK+ UI to talk to core */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2005 Elijah Newren * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_CORE_H #define META_CORE_H /* Don't include core headers here */ #include #include #include void meta_core_queue_frame_resize (Display *xdisplay, Window frame_xwindow); void meta_core_user_lower_and_unfocus (Display *xdisplay, Window frame_xwindow, guint32 timestamp); void meta_core_toggle_maximize (Display *xdisplay, Window frame_xwindow); void meta_core_toggle_maximize_horizontally (Display *xdisplay, Window frame_xwindow); void meta_core_toggle_maximize_vertically (Display *xdisplay, Window frame_xwindow); void meta_core_show_window_menu (Display *xdisplay, Window frame_xwindow, MetaWindowMenuType menu, int root_x, int root_y, guint32 timestamp); void meta_core_show_window_menu_for_rect (Display *xdisplay, Window frame_xwindow, MetaWindowMenuType menu, MetaRectangle *rect, guint32 timestamp); gboolean meta_core_begin_grab_op (Display *xdisplay, Window frame_xwindow, MetaGrabOp op, gboolean pointer_already_grabbed, gboolean frame_action, int button, gulong modmask, guint32 timestamp, int root_x, int root_y); void meta_core_end_grab_op (Display *xdisplay, guint32 timestamp); MetaGrabOp meta_core_get_grab_op (Display *xdisplay); void meta_core_grab_buttons (Display *xdisplay, Window frame_xwindow); void meta_core_set_screen_cursor (Display *xdisplay, Window frame_on_screen, MetaCursor cursor); void meta_invalidate_default_icons (void); void meta_retheme_all (void); #endif ukwm/src/core/bell.h0000664000175000017500000000626213220600404013266 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2002 Sun Microsystems Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include #include #include "display-private.h" #include "frame.h" /** * 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); /** * meta_bell_set_audible: * @display: The display we're configuring * @audible: True for an audible bell, false for a visual bell * * Turns the bell to audible or visual. This tells X what to do, but * not Ukwm; you will need to set the "visual bell" pref for that. * * If the configure script found we had no XKB, this is a no-op. */ void meta_bell_set_audible (MetaDisplay *display, gboolean audible); /** * meta_bell_init: * @display: The display which is opening * * 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? */ gboolean meta_bell_init (MetaDisplay *display); /** * meta_bell_shutdown: * @display: The display which is closing * * Shuts down the bell subsystem. * * \bug This is never called! If we had XkbSetAutoResetControls * enabled in meta_bell_init(), this wouldn't be a problem, but * we don't. */ void meta_bell_shutdown (MetaDisplay *display); /** * meta_bell_notify_frame_destroy: * @frame: The frame which is being destroyed * * Deals with a frame being destroyed. This is important because if we're * using a visual bell, we might be flashing the edges of the frame, and * so we'd have a timeout function waiting ready to un-flash them. If the * frame's going away, we can tell the timeout not to bother. */ void meta_bell_notify_frame_destroy (MetaFrame *frame); ukwm/src/core/meta-accel-parse.c0000664000175000017500000002374713233511035015462 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "meta-accel-parse.h" #include "keybindings-private.h" #include #include #include /* This is copied from GTK+ and modified to work with ukwm'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; } ukwm/src/core/meta-border.h0000664000175000017500000000425313233511035014554 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 */ ukwm/src/core/boxes-private.h0000664000175000017500000002506313220600404015140 0ustar fengfeng/* -*- 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 #include #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; /* 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 NorthWestGravity and StaticGravity. * Also, I lied a little bit--technically, you could use it in a MoveResize * operation if you muck with old_rect just right). */ void meta_rectangle_resize_with_gravity (const MetaRectangle *old_rect, MetaRectangle *rect, int 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. */ 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) */ 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() */ 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. */ gboolean meta_rectangle_could_fit_in_region ( const GList *spanning_rects, const MetaRectangle *rect); gboolean meta_rectangle_contained_in_region ( const GList *spanning_rects, const MetaRectangle *rect); gboolean meta_rectangle_overlaps_with_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. */ 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 */ 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. */ 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. */ 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. */ 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. */ 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. */ 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. */ 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. */ GList* meta_rectangle_find_nonintersected_monitor_edges ( const GList *monitor_rects, const GSList *all_struts); gboolean meta_rectangle_is_adjecent_to (MetaRectangle *rect, MetaRectangle *other); static inline ClutterRect meta_rectangle_to_clutter_rect (MetaRectangle *rect) { return (ClutterRect) { .origin = { .x = rect->x, .y = rect->y }, .size = { .width = rect->width, .height = rect->height } }; } #endif /* META_BOXES_PRIVATE_H */ ukwm/src/core/window.c0000664000175000017500000077135013227625534013703 0ustar fengfeng/* -*- 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 * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ /** * SECTION:window * @title: MetaWindow * @short_description: Ukwm X managed windows */ #include #include "window-private.h" #include "boxes-private.h" #include "edge-resistance.h" #include "util-private.h" #include "frame.h" #include #include "workspace-private.h" #include "stack.h" #include "keybindings-private.h" #include "ui.h" #include "place.h" #include #include #include "constraints.h" #include #include "core.h" #include #include #include #include #include #include "meta/compositor-ukwm.h" #include "x11/window-x11.h" #include "x11/window-props.h" #include "x11/xprops.h" #ifdef HAVE_WAYLAND #include "wayland/meta-window-wayland.h" #include "wayland/meta-wayland-surface.h" #include "wayland/meta-wayland-private.h" #endif #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" /* 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, 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); static void update_edge_constraints (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_UKWM_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, LAST_PROP, }; static GParamSpec *obj_props[LAST_PROP]; enum { WORKSPACE_CHANGED, FOCUS, RAISED, UNMANAGED, SIZE_CHANGED, POSITION_CHANGED, 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_UKWM_HINTS: g_value_set_string (value, win->ukwm_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; 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", "96 pixel sized icon", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_MINI_ICON] = g_param_spec_pointer ("mini-icon", "Mini Icon", "16 pixel sized icon", 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_UKWM_HINTS] = g_param_spec_string ("ukwm-hints", "_UKWM_HINTS", "Contents of the _UKWM_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 ("resizable", "Resizable", "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); g_object_class_install_properties (object_class, LAST_PROP, 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[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::size-changed: * @window: a #MetaWindow * * This is emitted when the position 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) { gboolean is_desktop_or_dock; if (!window->screen->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_screen_minimize_all_on_active_workspace_except (window->screen, window); meta_screen_unshow_desktop (window->screen); } } 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; meta_error_trap_push (window->display); if (should_be_mapped) { XMapWindow (window->display->xdisplay, window->xwindow); } else { XUnmapWindow (window->display->xdisplay, window->xwindow); window->unmaps_pending ++; } meta_error_trap_pop (window->display); } 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_autofree gchar *title = NULL; g_clear_pointer (&window->desc, g_free); if (window->title) title = g_utf8_substring (window->title, 0, MIN (10, g_utf8_strlen (window->title, -1))); if (window->client_type == META_WINDOW_CLIENT_TYPE_X11) { if (title) window->desc = g_strdup_printf ("0x%lx (%s)", window->xwindow, title); else window->desc = g_strdup_printf ("0x%lx", window->xwindow); } else { guint64 small_stamp = window->stamp - G_GUINT64_CONSTANT(0x100000000); if (title) window->desc = g_strdup_printf ("W%" G_GUINT64_FORMAT " (%s)", small_stamp, title); else 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); if (old) g_signal_emit_by_name (window->screen, "window-left-monitor", old->number, window); if (window->monitor) g_signal_emit_by_name (window->screen, "window-entered-monitor", window->monitor->number, window); } 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); } MetaWindow * _meta_window_shared_new (MetaDisplay *display, MetaScreen *screen, MetaWindowClientType client_type, MetaWaylandSurface *surface, Window xwindow, gulong existing_wm_state, MetaCompEffect effect, XWindowAttributes *attrs) { MetaWindow *window; 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) window = g_object_new (META_TYPE_WINDOW_X11, NULL); #ifdef HAVE_WAYLAND 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; /* this is in window->screen->display, but that's too annoying to * type */ 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; window->screen = screen; 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->screen->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->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->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_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->hidden = FALSE; 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->take_focus = FALSE; window->delete_window = FALSE; window->can_ping = FALSE; 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->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 (client_type == META_WINDOW_CLIENT_TYPE_X11) window->decorated = TRUE; else window->decorated = FALSE; 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->tile_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; } META_WINDOW_GET_CLASS (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_screen_apply_startup_properties (window->screen, 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_screen_get_workspace_by_index (window->screen, window->initial_workspace); } 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); set_workspace_state (window, window->transient_for->on_all_workspaces_requested, window->transient_for->workspace); } 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, window->screen->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 (!window->override_redirect) meta_stack_add (window->screen->stack, window); else 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 (screen->display->compositor, window); window->known_to_compositor = TRUE; /* Sync stack changes */ meta_stack_thaw (window->screen->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->screen->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) { GList *tmp; meta_verbose ("Unmanaging %s\n", window->desc); window->unmanaging = TRUE; #ifdef HAVE_WAYLAND /* This needs to happen for both Wayland and XWayland clients, * so it can't be in MetaWindowWayland. */ if (window->surface) { meta_wayland_surface_set_window (window->surface, NULL); window->surface = NULL; } #endif 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 (window->screen->closing && 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->screen->stack); group = meta_window_get_group (window); if (group) meta_group_update_layers (group); meta_stack_thaw (window->screen->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 (window->screen->active_workspace, NULL, 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) { meta_free_gslist_and_elements (window->struts); 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); } if (window->sync_request_timeout_id) { g_source_remove (window->sync_request_timeout_id); window->sync_request_timeout_id = 0; } 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); meta_window_free_delete_dialog (window); set_workspace_state (window, FALSE, NULL); g_assert (window->workspace == NULL); #ifndef G_DISABLE_CHECKS tmp = window->screen->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 (!window->override_redirect) meta_stack_remove (window->screen->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->screen->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_screen_queue_check_fullscreen (window->screen); 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) { 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 = window->screen->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) { #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, window->screen->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); 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); 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; if (aw->screen != bw->screen) return 0; /* don't care how they sort with respect to each other */ else return meta_stack_windows_cmp (aw->screen->stack, aw, bw); } static gboolean idle_calc_showing (gpointer data) { 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; } 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; } 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 () != G_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; if (!window->display->mouse_mode) meta_display_increment_focus_sentinel (window->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; 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 (!(window->input || window->take_focus)) { *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 () == G_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_would_be_covered (const MetaWindow *newbie) { MetaWorkspace *workspace = meta_window_get_workspace ((MetaWindow *)newbie); GList *tmp, *windows; windows = meta_workspace_list_windows (workspace); tmp = windows; while (tmp != NULL) { MetaWindow *w = tmp->data; if (w->wm_state_above && w != newbie) { /* We have found a window that is "above". Perhaps it overlaps. */ if (windows_overlap (w, newbie)) { g_list_free (windows); /* clean up... */ return TRUE; /* yes, it does */ } } tmp = tmp->next; } g_list_free (windows); return FALSE; /* none found */ } void meta_window_force_placement (MetaWindow *window) { 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; meta_window_move_resize_now (window); 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; 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_would_be_covered (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); } 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 () == G_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->screen->stack); window->hidden = FALSE; meta_stack_thaw (window->screen->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); } else { /* 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_display_increment_focus_sentinel (window->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_screen_queue_check_fullscreen (window->screen); #ifdef HAVE_WAYLAND if (did_show && window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND) meta_wayland_compositor_repick (meta_wayland_compositor_get_default ()); #endif /* * 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); } } static void meta_window_hide (MetaWindow *window) { 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->screen->stack); window->hidden = TRUE; meta_stack_thaw (window->screen->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 == window->screen->active_workspace && !my_workspace->showing_desktop) not_this_one = window; meta_workspace_focus_default_window (window->screen->active_workspace, not_this_one, timestamp); } if (did_hide) meta_screen_queue_check_fullscreen (window->screen); } 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_SIDE_BY_SIDE (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_maximize = TRUE; 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->in_fullscreen) meta_screen_queue_check_fullscreen (window->screen); 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; } if (window->tile_mode != META_TILE_NONE) { saved_rect = &window->saved_rect; window->maximized_vertically = FALSE; window->tile_mode = META_TILE_NONE; } 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); meta_window_move_resize_internal (window, (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION | META_MOVE_RESIZE_STATE_CHANGED), NorthWestGravity, window->unconstrained_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_screen_get_size (window->screen, &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_screen_get_monitor_geometry (window->screen, 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 meta_window_get_tile_fraction (MetaWindow *window, MetaTileMode tile_mode, double *fraction) { MetaWindow *tile_match; /* Make sure the tile match is up-to-date and matches the * passed in mode rather than the current state */ tile_match = meta_window_find_tile_match (window, tile_mode); if (tile_mode == META_TILE_NONE) *fraction = -1.; else if (tile_mode == META_TILE_MAXIMIZED) *fraction = 1.; else if (tile_match) *fraction = 1. - tile_match->tile_hfraction; else if (META_WINDOW_TILED_SIDE_BY_SIDE (window)) { if (window->tile_mode != tile_mode) *fraction = 1. - window->tile_hfraction; else *fraction = window->tile_hfraction; } else *fraction = .5; } static void meta_window_update_tile_fraction (MetaWindow *window, int new_w, int new_h) { MetaWindow *tile_match = window->tile_match; MetaRectangle work_area; if (!META_WINDOW_TILED_SIDE_BY_SIDE (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; if (tile_match && window->display->grab_window == window) meta_window_tile (tile_match, tile_match->tile_mode); } static void update_edge_constraints (MetaWindow *window) { switch (window->tile_mode) { case META_TILE_NONE: window->edge_constraints[0] = META_EDGE_CONSTRAINT_NONE; window->edge_constraints[1] = META_EDGE_CONSTRAINT_NONE; window->edge_constraints[2] = META_EDGE_CONSTRAINT_NONE; window->edge_constraints[3] = META_EDGE_CONSTRAINT_NONE; break; case META_TILE_MAXIMIZED: window->edge_constraints[0] = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints[1] = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints[2] = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints[3] = META_EDGE_CONSTRAINT_MONITOR; break; case META_TILE_LEFT: window->edge_constraints[0] = META_EDGE_CONSTRAINT_MONITOR; if (window->tile_match) window->edge_constraints[1] = META_EDGE_CONSTRAINT_WINDOW; else window->edge_constraints[1] = META_EDGE_CONSTRAINT_NONE; window->edge_constraints[2] = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints[3] = META_EDGE_CONSTRAINT_MONITOR; break; case META_TILE_RIGHT: window->edge_constraints[0] = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints[1] = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints[2] = META_EDGE_CONSTRAINT_MONITOR; if (window->tile_match) window->edge_constraints[3] = META_EDGE_CONSTRAINT_WINDOW; else window->edge_constraints[3] = META_EDGE_CONSTRAINT_NONE; break; } /* h/vmaximize also modify the edge constraints */ if (window->maximized_vertically) { window->edge_constraints[0] = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints[2] = META_EDGE_CONSTRAINT_MONITOR; } if (window->maximized_horizontally) { window->edge_constraints[1] = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints[3] = META_EDGE_CONSTRAINT_MONITOR; } } void meta_window_tile (MetaWindow *window, MetaTileMode tile_mode) { MetaMaximizeFlags directions; MetaRectangle old_frame_rect, old_buffer_rect; meta_window_get_tile_fraction (window, tile_mode, &window->tile_hfraction); window->tile_mode = tile_mode; /* Don't do anything if no tiling is requested */ if (window->tile_mode == META_TILE_NONE) return; if (window->tile_mode == META_TILE_MAXIMIZED) directions = META_MAXIMIZE_BOTH; else directions = META_MAXIMIZE_VERTICAL; meta_window_maximize_internal (window, directions, NULL); meta_screen_update_tile_preview (window->screen, FALSE); /* Setup the edge constraints */ update_edge_constraints (window); 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); meta_window_move_resize_internal (window, (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION | META_MOVE_RESIZE_STATE_CHANGED), NorthWestGravity, window->unconstrained_rect); if (window->frame) meta_frame_queue_draw (window->frame); } void meta_window_restore_tile (MetaWindow *window, MetaTileMode mode, int width, int height) { meta_window_update_tile_fraction (window, width, height); meta_window_tile (window, mode); } static gboolean meta_window_can_tile_maximized (MetaWindow *window) { return window->has_maximize_func; } gboolean meta_window_can_tile_side_by_side (MetaWindow *window) { int monitor; MetaRectangle tile_area; MetaRectangle client_rect; if (!meta_window_can_tile_maximized (window)) return FALSE; monitor = meta_screen_get_current_monitor (window->screen); meta_window_get_work_area_for_monitor (window, monitor, &tile_area); /* Do not allow tiling in portrait orientation */ if (tile_area.height > tile_area.width) return FALSE; tile_area.width /= 2; meta_window_frame_rect_to_client_rect (window, &tile_area, &client_rect); return client_rect.width >= window->size_hints.min_width && client_rect.height >= window->size_hints.min_height; } 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); } else if (window->screen->closing /* See bug #358042 */ #ifdef HAVE_WAYLAND && !meta_is_wayland_compositor () #endif ) { /* 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); } } 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_horizontally && unmaximize_vertically) window->saved_maximize = FALSE; /* 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; 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_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); 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), NorthWestGravity, 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_screen_queue_check_fullscreen (window->screen); } 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->screen->stack); meta_window_raise (window); meta_stack_thaw (window->screen->stack); meta_window_recalc_features (window); set_net_wm_state (window); /* For the auto-minimize feature, if we fail to get focus */ meta_screen_queue_check_fullscreen (window->screen); 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), NorthWestGravity, 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_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); /* 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), NorthWestGravity, target_rect); meta_screen_queue_check_fullscreen (window->screen); 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_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) { gboolean allow_workspace_switch; 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)) { 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 = window->screen->active_workspace; /* For non-transient windows, we just set up a pulsing indicator, rather than move windows or workspaces. See http://bugzilla.gnome.org/show_bug.cgi?id=482354 */ if (window->transient_for == NULL && !allow_workspace_switch && !meta_window_located_on_workspace (window, workspace)) { meta_window_set_demands_attention (window); /* We've marked it as demanding, don't need to do anything else. */ return; } 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 Ukwm, but Ukwm 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) { 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; } static gboolean maybe_move_attached_dialog (MetaWindow *window, void *data) { if (meta_window_is_attached_dialog (window)) /* It ignores x,y for such a dialog */ meta_window_move_frame (window, FALSE, 0, 0); return FALSE; } /** * 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 */ int meta_window_get_monitor (MetaWindow *window) { 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, guint 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, FALSE); return; } 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 (new && old) { if (window->tile_mode != META_TILE_NONE) window->tile_monitor_number = new->number; /* 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, &old->rect, &new->rect); } else { meta_window_update_monitor (window, FALSE); } } void meta_window_update_monitor (MetaWindow *window, gboolean user_op) { const MetaLogicalMonitor *old; old = window->monitor; META_WINDOW_GET_CLASS (window)->update_main_monitor (window, user_op); 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 () && user_op && meta_window_is_on_primary_monitor (window) && window->screen->active_workspace != window->workspace) meta_window_change_workspace (window, window->screen->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, int 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. */ gboolean did_placement; MetaRectangle unconstrained_rect; MetaRectangle constrained_rect; MetaMoveResizeResultFlags result = 0; gboolean moved_or_resized = FALSE; 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_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_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; if (flags & (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION) && window->monitor) { MetaRectangle old_rect; meta_window_get_frame_rect (window, &old_rect); meta_window_constrain (window, flags, gravity, &old_rect, &constrained_rect); } /* 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, 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 || (flags & META_MOVE_RESIZE_STATE_CHANGED) != 0) && window->known_to_compositor) { meta_compositor_sync_window_geometry (window->display->compositor, window, did_placement); } if (window->monitor) { guint old_output_winsys_id; old_output_winsys_id = window->monitor->winsys_id; meta_window_update_monitor (window, flags & META_MOVE_RESIZE_USER_ACTION); 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, flags & META_MOVE_RESIZE_USER_ACTION); } 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_dialog, NULL); meta_stack_update_window_tile_matches (window->screen->stack, window->screen->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 }; 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, NorthWestGravity, rect); } static void meta_window_move_between_rects (MetaWindow *window, 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_now (window); } /** * 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, NorthWestGravity, 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, NULL, &new_area); } else { if (monitor == window->monitor->number) return; meta_window_move_between_rects (window, &old_area, &new_area); } window->preferred_output_winsys_id = window->monitor->winsys_id; if (window->fullscreen || window->override_redirect) meta_screen_queue_check_fullscreen (window->screen); } static void adjust_size_for_tile_match (MetaWindow *window, int *new_w, int *new_h) { MetaRectangle work_area, rect; MetaWindow *tile_match = window->tile_match; if (!META_WINDOW_TILED_SIDE_BY_SIDE (window) || !tile_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; meta_window_frame_rect_to_client_rect (window, &rect, &rect); *new_w += MAX(0, window->size_hints.min_width - rect.width); /* Make sure we're not resizing the tile match below its min width */ rect = work_area; rect.width = work_area.width - *new_w; meta_window_frame_rect_to_client_rect (tile_match, &rect, &rect); *new_w -= MAX(0, tile_match->size_hints.min_width - rect.width); } void meta_window_resize_frame_with_gravity (MetaWindow *window, gboolean user_op, int w, int h, int 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_fraction (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, int 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 == StaticGravity) { 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 NorthGravity: case CenterGravity: case SouthGravity: /* Find center of frame. */ x += frame_extents.width / 2; /* Center client window on that point. */ x -= w / 2; break; case SouthEastGravity: case EastGravity: case NorthEastGravity: /* 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 WestGravity: case CenterGravity: case EastGravity: /* Find center of frame. */ y += frame_extents.height / 2; /* Center client window there. */ y -= h / 2; break; case SouthWestGravity: case SouthGravity: case SouthEastGravity: /* 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) { MetaWindow *modal_transient; g_return_if_fail (!window->override_redirect); meta_topic (META_DEBUG_FOCUS, "Setting input focus to window %s, input: %d take_focus: %d\n", window->desc, window->input, window->take_focus); if (window->display->grab_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, window->screen->active_workspace)) meta_window_change_workspace (modal_transient, window->screen->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) { /* If we're on all workspaces, then our new workspace must be NULL. */ if (on_all_workspaces) g_assert (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 = window->screen->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 = window->screen->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); } 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) { 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 = window->screen->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->screen->stack == ancestor->screen->stack) { /* If the window has a tile sibling, raise it before raising the window itself */ if (window->tile_match) meta_stack_raise (window->tile_match->screen->stack, window->tile_match); meta_stack_raise (window->screen->stack, ancestor); } else { meta_warning ( "Either stacks aren't per screen or some window has a weird " "transient_for hint; window->screen->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->screen->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); /* If the window has a tile sibling, lower it before loweting the window itself */ if (window->tile_match) meta_stack_lower (window->tile_match->screen->stack, window->tile_match); meta_stack_lower (window->screen->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) { MetaWorkspace *workspace; MetaScreen *screen; g_return_if_fail (!window->override_redirect); if (space_index == -1) { meta_window_stick (window); return; } screen = window->screen; workspace = meta_screen_get_workspace_by_index (screen, space_index); if (!workspace && append) workspace = meta_screen_append_new_workspace (screen, FALSE, CurrentTime); 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) { 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 (window->screen->active_workspace && meta_window_located_on_workspace (window, window->screen->active_workspace)) { GList* link; link = g_list_find (window->screen->active_workspace->mru_list, window); g_assert (link); window->screen->active_workspace->mru_list = g_list_remove_link (window->screen->active_workspace->mru_list, link); g_list_free (link); window->screen->active_workspace->mru_list = g_list_prepend (window->screen->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 () == G_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 () == G_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 cairo_surface_t * load_default_window_icon (int size) { GtkIconTheme *theme = gtk_icon_theme_get_default (); GdkPixbuf *pixbuf; const char *icon_name; if (gtk_icon_theme_has_icon (theme, META_DEFAULT_ICON_NAME)) icon_name = META_DEFAULT_ICON_NAME; else icon_name = "image-missing"; pixbuf = gtk_icon_theme_load_icon (theme, icon_name, size, 0, NULL); return gdk_cairo_surface_create_from_pixbuf (pixbuf, 1, NULL); } static cairo_surface_t * get_default_window_icon (void) { static cairo_surface_t *default_icon = NULL; if (default_icon == NULL) { default_icon = load_default_window_icon (META_ICON_WIDTH); g_assert (default_icon); } return cairo_surface_reference (default_icon); } static cairo_surface_t * get_default_mini_icon (void) { static cairo_surface_t *default_icon = NULL; if (default_icon == NULL) { default_icon = load_default_window_icon (META_MINI_ICON_WIDTH); g_assert (default_icon); } return cairo_surface_reference (default_icon); } 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); if (icon) window->icon = icon; else window->icon = get_default_window_icon (); if (window->mini_icon) cairo_surface_destroy (window->mini_icon); if (mini_icon) window->mini_icon = mini_icon; else window->mini_icon = get_default_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); } g_assert (window->icon); g_assert (window->mini_icon); } 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) { if (window->on_all_workspaces) return window->screen->workspaces; else if (window->workspace != NULL) return window->workspace->list_containing_self; else if (window->constructing) return NULL; else g_assert_not_reached (); } 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; /* 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 == window->screen->rect.width && window->size_hints.min_height == window->screen->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]); /* 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) { 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 = window->screen->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) { 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 = window->screen->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 double timeval_to_ms (const GTimeVal *timeval) { return (timeval->tv_sec * G_USEC_PER_SEC + timeval->tv_usec) / 1000.0; } static double time_diff (const GTimeVal *first, const GTimeVal *second) { double first_ms = timeval_to_ms (first); double second_ms = timeval_to_ms (second); return first_ms - second_ms; } static gboolean check_moveresize_frequency (MetaWindow *window, gdouble *remaining) { GTimeVal current_time; const double max_resizes_per_second = 25.0; const double ms_between_resizes = 1000.0 / max_resizes_per_second; double elapsed; g_get_current_time (¤t_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 = time_diff (¤t_time, &window->display->grab_last_moveresize_time); 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; } 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; MetaScreen *screen = window->screen; 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_side_by_side (window) && x >= logical_monitor->rect.x && x < (work_area.x + shake_threshold)) screen->preview_tile_mode = META_TILE_LEFT; else if (meta_window_can_tile_side_by_side (window) && x >= work_area.x + work_area.width - shake_threshold && x < (logical_monitor->rect.x + logical_monitor->rect.width)) screen->preview_tile_mode = META_TILE_RIGHT; else if (meta_window_can_tile_maximized (window) && y >= logical_monitor->rect.y && y <= work_area.y) screen->preview_tile_mode = META_TILE_MAXIMIZED; else screen->preview_tile_mode = META_TILE_NONE; if (screen->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; MetaScreen *screen = window->screen; 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. */ screen->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_SIDE_BY_SIDE (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. */ if ((META_WINDOW_MAXIMIZED (window) && ABS (dy) >= shake_threshold) || (META_WINDOW_TILED_SIDE_BY_SIDE (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 (); window->tile_mode = META_TILE_NONE; /* 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 || META_WINDOW_MAXIMIZED (window)) && window->tile_mode != META_TILE_LEFT && window->tile_mode != META_TILE_RIGHT) { 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; window->tile_mode = META_TILE_NONE; 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 && y < (work_area.y + shake_threshold)) { /* move the saved rect if window will become maximized on an * other monitor so user isn't surprised on a later unmaximize */ if (wmonitor->number != monitor) { 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; 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_screen_update_tile_preview (screen, screen->preview_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_SIDE_BY_SIDE (window)) new_x = old.x; if (window->maximized_vertically) 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; int new_w, new_h; int gravity; MetaRectangle old; 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_w = window->display->grab_anchor_window_pos.width; new_h = 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_w += dx; else if (window->display->grab_op & META_GRAB_OP_WINDOW_DIR_WEST) new_w -= dx; if (window->display->grab_op & META_GRAB_OP_WINDOW_DIR_SOUTH) new_h += dy; else if (window->display->grab_op & META_GRAB_OP_WINDOW_DIR_NORTH) new_h -= dy; /* 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, "[ukwm] update_resize_timeout"); } return; } /* Remove any scheduled compensation events */ if (window->display->grab_resize_timeout_id) { g_source_remove (window->display->grab_resize_timeout_id); window->display->grab_resize_timeout_id = 0; } meta_window_get_frame_rect (window, &old); /* 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_w = old.width; if ((window->display->grab_op & (META_GRAB_OP_WINDOW_DIR_NORTH | META_GRAB_OP_WINDOW_DIR_SOUTH)) == 0) new_h = old.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_w, &new_h, gravity, update_resize_timeout, snap, FALSE); meta_window_resize_frame_with_gravity (window, TRUE, new_w, new_h, gravity); /* Store the latest resize time, if we actually resized. */ if (window->rect.width != old.width || window->rect.height != old.height) g_get_current_time (&window->display->grab_last_moveresize_time); } static void maybe_maximize_tiled_window (MetaWindow *window) { MetaRectangle work_area; gint shake_threshold; if (!META_WINDOW_TILED_SIDE_BY_SIDE (window)) return; shake_threshold = meta_prefs_get_drag_threshold (); meta_window_get_work_area_for_monitor (window, window->tile_monitor_number, &work_area); if (window->rect.width >= work_area.width - shake_threshold) meta_window_maximize (window, META_MAXIMIZE_BOTH); } 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->screen->preview_tile_mode != META_TILE_NONE) meta_window_tile (window, window->screen->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->tile_match != NULL, x, y, TRUE); maybe_maximize_tiled_window (window); } } window->screen->preview_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->tile_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; /* Initialize to the whole screen */ *area = window->screen->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 fraction; 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_fraction (window, tile_mode, &fraction); *tile_area = work_area; tile_area->width = round (tile_area->width * fraction); if (tile_mode == META_TILE_RIGHT) tile_area->x += work_area.width - tile_area->width; } 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; MetaDisplay *display; display = window->display; /* 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, window->screen->rect.width-1); *y = CLAMP (*y, 0, window->screen->rect.height-1); meta_error_trap_push (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); { MetaBackend *backend = meta_get_backend (); meta_backend_warp_pointer (backend, *x, *y); } if (meta_error_trap_pop_with_return (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->screen, 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); } void meta_window_update_layer (MetaWindow *window) { MetaGroup *group; meta_stack_freeze (window->screen->stack); group = meta_window_get_group (window); if (group) meta_group_update_layers (group); else meta_stack_update_layer (window->screen->stack, window); meta_stack_thaw (window->screen->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. */ GList* active_mru_list; GList* window_position; GList* after_this_one_position; active_mru_list = window->screen->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)) { window->screen->active_workspace->mru_list = g_list_delete_link (window->screen->active_workspace->mru_list, window_position); window->screen->active_workspace->mru_list = g_list_insert_before (window->screen->active_workspace->mru_list, after_this_one_position->next, window); } } 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 Ukwm initially upon window creation, * and updated thereafter on input events (key and button presses) seen by Ukwm, * 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 () == G_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) { MetaRectangle candidate_rect, other_rect; GList *stack = window->screen->stack->sorted; MetaWindow *other_window; gboolean obscured = FALSE; MetaWorkspace *workspace = window->screen->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 ukwm; it will * control its own positioning and ukwm 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_screen: * @window: a #MetaWindow * * Gets the #MetaScreen that the window is on. * * Return value: (transfer none): the #MetaScreen for the window */ MetaScreen * meta_window_get_screen (MetaWindow *window) { return window->screen; } /** * 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: (skip) * @window: a #MetaWindow * */ 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) { if (window->on_all_workspaces) return window->screen->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_flatpak_id: * @window: a #MetaWindow * * Return value: (transfer none): the Flatpak application ID or %NULL **/ const char * meta_window_get_flatpak_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_display_lookup_x_window (window->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 ukwm. */ gboolean meta_window_is_remote (MetaWindow *window) { return window->is_remote; } /** * meta_window_get_ukwm_hints: * @window: a #MetaWindow * * Gets the current value of the _UKWM_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; 'ukwm-' key prefix is reserved for internal use, and must not be used * by plugins. * * Return value: (transfer none): the _UKWM_HINTS string, or %NULL if no hints * are set. */ const char * meta_window_get_ukwm_hints (MetaWindow *window) { g_return_val_if_fail (META_IS_WINDOW (window), NULL); return window->ukwm_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 || (window->hide_titlebar_when_maximized && META_WINDOW_MAXIMIZED (window)) || (window->hide_titlebar_when_maximized && META_WINDOW_TILED_SIDE_BY_SIDE (window))) { /* 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): the matching tiled window or * %NULL if it doesn't exist. */ MetaWindow * meta_window_get_tile_match (MetaWindow *window) { return window->tile_match; } void meta_window_compute_tile_match (MetaWindow *window) { window->tile_match = meta_window_find_tile_match (window, window->tile_mode); } static MetaWindow * meta_window_find_tile_match (MetaWindow *window, MetaTileMode current_mode) { MetaWindow *match; MetaStack *stack; MetaTileMode match_tile_mode = META_TILE_NONE; if (window->shaded || window->minimized) return NULL; if (current_mode == META_TILE_LEFT) match_tile_mode = META_TILE_RIGHT; else if (current_mode == META_TILE_RIGHT) match_tile_mode = META_TILE_LEFT; else return NULL; stack = window->screen->stack; for (match = meta_stack_get_top (stack); match; match = meta_stack_get_below (stack, match, FALSE)) { if (!match->shaded && !match->minimized && match->tile_mode == match_tile_mode && 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->screen->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); /* * 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 */ if (window->tile_mode == current_mode && !(meta_grab_op_is_resizing (window->display->grab_op) && window->display->grab_window == window && window->tile_match != NULL)) { int threshold = meta_prefs_get_drag_threshold (); 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; } /* * 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; } 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; } } } /* 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->screen->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) { window->opacity = opacity; 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() == G_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_focus_the_no_focus_window (display, window->screen, timestamp); } } } static gboolean window_has_pointer_wayland (MetaWindow *window) { ClutterDeviceManager *dm; ClutterInputDevice *dev; ClutterActor *pointer_actor, *window_actor; dm = clutter_device_manager_get_default (); dev = clutter_device_manager_get_core_device (dm, CLUTTER_POINTER_DEVICE); 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) { MetaDisplay *display = window->display; MetaScreen *screen = window->screen; Window root, child; double root_x, root_y, x, y; XIButtonState buttons; XIModifierState mods; XIGroupState group; meta_error_trap_push (display); XIQueryPointer (display->xdisplay, META_VIRTUAL_CORE_POINTER_ID, screen->xroot, &root, &child, &root_x, &root_y, &x, &y, &buttons, &mods, &group); meta_error_trap_pop (display); free (buttons.mask); return meta_display_lookup_x_window (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 () == G_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; if (display->focus_timeout_id != 0) g_source_remove (display->focus_timeout_id); 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, "[ukwm] 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 G_DESKTOP_FOCUS_MODE_SLOPPY: case G_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 G_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); } void meta_window_handle_ungrabbed_event (MetaWindow *window, const ClutterEvent *event) { MetaDisplay *display = window->display; gboolean unmodified; gboolean is_window_grab; ClutterModifierType grab_mods, event_mods; gfloat x, y; guint button; if (window->frame && meta_ui_frame_handle_event (window->frame->ui_frame, event)) 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); } 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; is_window_grab = (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->screen, 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->screen, 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); } ukwm/src/core/place.h0000664000175000017500000000261513220600404013432 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm window placement */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_PLACE_H #define META_PLACE_H #include "window-private.h" #include "frame.h" void meta_window_process_placement (MetaWindow *window, MetaPlacementRule *placement_rule, int *x, int *y); void meta_window_place (MetaWindow *window, int x, int y, int *new_x, int *new_y); #endif ukwm/src/core/ukwm.c0000664000175000017500000000446013220600404013324 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2011 Red Hat, Inc. * Copyright 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include "config.h" #include #include #include #include #include "meta-plugin-manager.h" #include static gboolean print_version (const gchar *option_name, const gchar *value, gpointer data, GError **error) { const int latest_year = 2011; g_print (_("ukwm %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 = "default"; GOptionEntry ukwm_options[] = { { "version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, print_version, N_("Print version"), NULL }, { "ukwm-plugin", 0, 0, G_OPTION_ARG_STRING, &plugin, N_("Ukwm 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, ukwm_options, GETTEXT_PACKAGE); if (!g_option_context_parse (ctx, &argc, &argv, &error)) { g_printerr ("ukwm: %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 (); } ukwm/src/core/meta-gesture-tracker.c0000664000175000017500000004211113233511035016374 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 "meta-gesture-tracker-private.h" #include "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; guint gesture_begin_id; guint gesture_end_id; guint 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, LAST_PROP, }; static GParamSpec *obj_props[LAST_PROP]; 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, LAST_PROP, 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) { if (info->autodeny_timeout_id) g_source_remove (info->autodeny_timeout_id); 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_signal_handler_disconnect (data->gesture, data->gesture_begin_id); g_signal_handler_disconnect (data->gesture, data->gesture_end_id); g_signal_handler_disconnect (data->gesture, data->gesture_cancel_id); /* 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 */ if (info->autodeny_timeout_id) { g_source_remove (info->autodeny_timeout_id); info->autodeny_timeout_id = 0; } 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); } ukwm/src/core/screen-private.h0000664000175000017500000001564213220600404015301 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * \file screen-private.h Screens which Ukwm manages * * Managing X screens. * This file contains methods on this class which are available to * routines in core but not outside it. (See screen.h for the routines * which the rest of the world is allowed to use.) */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_SCREEN_PRIVATE_H #define META_SCREEN_PRIVATE_H #include "display-private.h" #include #include #include "stack-tracker.h" #include "ui.h" #include "meta-monitor-manager-private.h" typedef void (* MetaScreenWindowFunc) (MetaWindow *window, gpointer user_data); #define META_WIREFRAME_XOR_LINE_WIDTH 2 struct _MetaScreen { GObject parent_instance; MetaDisplay *display; char *screen_name; Window xroot; int default_depth; Visual *default_xvisual; MetaRectangle rect; /* Size of screen; rect.x & rect.y are always 0 */ MetaUI *ui; guint tile_preview_timeout_id; guint preview_tile_mode : 2; MetaWorkspace *active_workspace; /* This window holds the focus when we don't want to focus * any actual clients */ Window no_focus_window; GList *workspaces; MetaStack *stack; MetaStackTracker *stack_tracker; MetaCursor current_cursor; Window wm_sn_selection_window; Atom wm_sn_atom; guint32 wm_sn_timestamp; gboolean has_xinerama_indices; GSList *startup_sequences; Window wm_cm_selection_window; guint work_area_later; guint check_fullscreen_later; int rows_of_workspaces; int columns_of_workspaces; MetaScreenCorner starting_corner; guint vertical_workspaces : 1; guint workspace_layout_overridden : 1; guint keys_grabbed : 1; int closing; /* 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 composite_overlay_window; }; struct _MetaScreenClass { GObjectClass parent_class; void (*restacked) (MetaScreen *); void (*workareas_changed) (MetaScreen *); void (*monitors_changed) (MetaScreen *); }; MetaScreen* meta_screen_new (MetaDisplay *display, guint32 timestamp); void meta_screen_free (MetaScreen *screen, guint32 timestamp); void meta_screen_init_workspaces (MetaScreen *screen); void meta_screen_manage_all_windows (MetaScreen *screen); void meta_screen_foreach_window (MetaScreen *screen, MetaListWindowsFlags flags, MetaScreenWindowFunc func, gpointer data); void meta_screen_update_cursor (MetaScreen *screen); void meta_screen_update_tile_preview (MetaScreen *screen, gboolean delay); void meta_screen_hide_tile_preview (MetaScreen *screen); MetaWindow* meta_screen_get_mouse_window (MetaScreen *screen, MetaWindow *not_this_one); void meta_screen_update_workspace_layout (MetaScreen *screen); void meta_screen_update_workspace_names (MetaScreen *screen); void meta_screen_queue_workarea_recalc (MetaScreen *screen); void meta_screen_queue_check_fullscreen (MetaScreen *screen); Window meta_create_offscreen_window (Display *xdisplay, Window parent, long valuemask); typedef struct MetaWorkspaceLayout MetaWorkspaceLayout; struct MetaWorkspaceLayout { int rows; int cols; int *grid; int grid_area; int current_row; int current_col; }; void meta_screen_calc_workspace_layout (MetaScreen *screen, int num_workspaces, int current_space, MetaWorkspaceLayout *layout); void meta_screen_free_workspace_layout (MetaWorkspaceLayout *layout); void meta_screen_minimize_all_on_active_workspace_except (MetaScreen *screen, MetaWindow *keep); /* Show/hide the desktop (temporarily hide all windows) */ void meta_screen_show_desktop (MetaScreen *screen, guint32 timestamp); void meta_screen_unshow_desktop (MetaScreen *screen); /* Update whether the destkop is being shown for the current active_workspace */ void meta_screen_update_showing_desktop_hint (MetaScreen *screen); gboolean meta_screen_apply_startup_properties (MetaScreen *screen, MetaWindow *window); void meta_screen_restacked (MetaScreen *screen); void meta_screen_workspace_switched (MetaScreen *screen, int from, int to, MetaMotionDirection direction); void meta_screen_set_active_workspace_hint (MetaScreen *screen); void meta_screen_create_guard_window (MetaScreen *screen); gboolean meta_screen_handle_xevent (MetaScreen *screen, XEvent *xevent); MetaLogicalMonitor * meta_screen_xinerama_index_to_logical_monitor (MetaScreen *screen, int index); int meta_screen_logical_monitor_to_xinerama_index (MetaScreen *screen, MetaLogicalMonitor *logical_monitor); #endif ukwm/src/core/place.c0000664000175000017500000006703613220600404013435 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm window placement */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002, 2003 Red Hat, Inc. * Copyright (C) 2003 Rob Adams * Copyright (C) 2005 Elijah Newren * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include #include "boxes-private.h" #include "place.h" #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include #include #include #include #include #include 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 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_center_new_windows ())); } 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 *x, int *y) { MetaWindow *parent = meta_window_get_transient_for (window); MetaRectangle parent_rect; MetaRectangle anchor_rect; int window_width, window_height; window_width = placement_rule->width; window_height = placement_rule->height; meta_window_get_frame_rect (parent, &parent_rect); anchor_rect = (MetaRectangle) { .x = parent_rect.x + placement_rule->anchor_rect.x, .y = parent_rect.y + placement_rule->anchor_rect.y, .width = placement_rule->anchor_rect.width, .height = placement_rule->anchor_rect.height, }; /* 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; } 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; meta_topic (META_DEBUG_PLACEMENT, "Placing window %s\n", window->desc); /* If the window has a custom placement rule, always run only that. */ if (window->placement_rule) { meta_window_process_placement (window, window->placement_rule, &x, &y); goto done; } 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; 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; } ukwm/src/core/display-private.h0000664000175000017500000004550613220600404015471 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm 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 * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_DISPLAY_PRIVATE_H #define META_DISPLAY_PRIVATE_H #ifndef PACKAGE #error "config.h not included" #endif #include #include #include #include #include #include "keybindings-private.h" #include "startup-notification-private.h" #include "meta-gesture-tracker-private.h" #include #include #include #ifdef HAVE_STARTUP_NOTIFICATION #include #endif #include typedef struct _MetaStack MetaStack; typedef struct _MetaUISlave MetaUISlave; typedef struct _MetaGroupPropHooks MetaGroupPropHooks; typedef struct _MetaWindowPropHooks MetaWindowPropHooks; typedef struct MetaEdgeResistanceData MetaEdgeResistanceData; 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; #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 { META_TILE_NONE, META_TILE_LEFT, META_TILE_RIGHT, META_TILE_MAXIMIZED } MetaTileMode; 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 gboolean (*MetaAlarmFilter) (MetaDisplay *display, XSyncAlarmNotifyEvent *event, gpointer data); struct _MetaDisplay { GObject parent_instance; char *name; Display *xdisplay; int clutter_event_filter; Window leader_window; Window timestamp_pinging_window; /* 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 #undef item /* The window and serial of the most recent FocusIn event. */ Window server_focus_window; gulong server_focus_serial; /* 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; /* 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; /* 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; /* 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; /*< private-ish >*/ MetaScreen *screen; GHashTable *xids; 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 ukwm. * * 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. */ GTimeVal grab_last_moveresize_time; MetaEdgeResistanceData *grab_edge_resistance_data; unsigned int grab_last_user_action_was_snap; /* we use property updates as sentinels for certain window focus events * to avoid some race conditions on EnterNotify events */ int sentinel_counter; int xkb_base_event_type; guint32 last_bell_time; 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 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; /* Managed by compositor.c */ MetaCompositor *compositor; MetaGestureTracker *gesture_tracker; ClutterEventSequence *pointer_emulating_sequence; MetaAlarmFilter alarm_filter; gpointer alarm_filter_data; 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; ClutterActor *current_pad_osd; MetaStartupNotification *startup_notification; int xsync_event_base; int xsync_error_base; int shape_event_base; int shape_error_base; unsigned int have_xsync : 1; #define META_DISPLAY_HAS_XSYNC(display) ((display)->have_xsync) unsigned int have_shape : 1; #define META_DISPLAY_HAS_SHAPE(display) ((display)->have_shape) unsigned int have_composite : 1; unsigned int have_damage : 1; #define META_DISPLAY_HAS_COMPOSITE(display) ((display)->have_composite) #define META_DISPLAY_HAS_DAMAGE(display) ((display)->have_damage) #ifdef HAVE_XI23 gboolean have_xinput_23 : 1; #define META_DISPLAY_HAS_XINPUT_23(display) ((display)->have_xinput_23) #else #define META_DISPLAY_HAS_XINPUT_23(display) FALSE #endif /* HAVE_XI23 */ }; 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_close (MetaDisplay *display, guint32 timestamp); void meta_display_unmanage_windows_for_screen (MetaDisplay *display, MetaScreen *screen, guint32 timestamp); /* Utility function to compare the stacking of two windows */ int meta_display_stack_cmp (const void *a, const void *b); /* A given MetaWindow may have various X windows that "belong" * to it, such as the frame window. */ MetaWindow* meta_display_lookup_x_window (MetaDisplay *display, Window xwindow); void meta_display_register_x_window (MetaDisplay *display, Window *xwindowp, MetaWindow *window); void meta_display_unregister_x_window (MetaDisplay *display, Window xwindow); /* 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)) 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); MetaWindow* meta_display_lookup_sync_alarm (MetaDisplay *display, XSyncAlarm alarm); void meta_display_register_sync_alarm (MetaDisplay *display, XSyncAlarm *alarmp, MetaWindow *window); void meta_display_unregister_sync_alarm (MetaDisplay *display, XSyncAlarm alarm); void meta_display_notify_window_created (MetaDisplay *display, MetaWindow *window); GSList* meta_display_list_windows (MetaDisplay *display, MetaListWindowsFlags flags); MetaDisplay* meta_display_for_x_display (Display *xdisplay); MetaDisplay* meta_get_display (void); Cursor meta_display_create_x_cursor (MetaDisplay *display, MetaCursor cursor); 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); /* make a request to ensure the event serial has changed */ void meta_display_increment_event_serial (MetaDisplay *display); void meta_display_update_active_window_hint (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_retheme_all (void); void meta_display_ping_window (MetaWindow *window, guint32 serial); void meta_display_pong_for_serial (MetaDisplay *display, guint32 serial); int 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_increment_focus_sentinel (MetaDisplay *display); void meta_display_decrement_focus_sentinel (MetaDisplay *display); gboolean meta_display_focus_sentinel_clear (MetaDisplay *display); 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); #ifdef HAVE_XI23 gboolean meta_display_process_barrier_xevent (MetaDisplay *display, XIEvent *event); #endif /* HAVE_XI23 */ void meta_display_set_input_focus_xwindow (MetaDisplay *display, MetaScreen *screen, Window window, guint32 timestamp); void meta_display_sync_wayland_input_focus (MetaDisplay *display); void meta_display_update_focus_window (MetaDisplay *display, MetaWindow *window, Window xwindow, gulong serial, gboolean focused_by_us); 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_restart_init (void); void meta_restart_finish (void); void meta_display_cancel_touch (MetaDisplay *display); gboolean meta_display_windows_are_interactable (MetaDisplay *display); void meta_display_set_alarm_filter (MetaDisplay *display, MetaAlarmFilter filter, gpointer data); 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); #endif ukwm/src/core/display.c0000664000175000017500000027152713220600404014020 0ustar fengfeng/* -*- 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) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ /** * SECTION:display * @title: MetaDisplay * @short_description: Ukwm X display handler * * The display is represented as a #MetaDisplay struct. */ #define _XOPEN_SOURCE 600 /* for gethostname() */ #include #include "display-private.h" #include "events.h" #include "util-private.h" #include #include "screen-private.h" #include "window-private.h" #include "frame.h" #include #include "keybindings-private.h" #include #include "workspace-private.h" #include "bell.h" #include #include #include #include #include "meta-idle-monitor-dbus.h" #include "meta-cursor-tracker-private.h" #include #include "backends/meta-logical-monitor.h" #include "backends/native/meta-backend-native.h" #include "backends/x11/meta-backend-x11.h" #include "backends/meta-stage.h" #include "backends/meta-input-settings-private.h" #include #ifdef HAVE_RANDR #include #endif #include #include #include #include #include #include #include #include #include "x11/events.h" #include "x11/window-x11.h" #include "x11/window-props.h" #include "x11/group-props.h" #include "x11/xprops.h" #ifdef HAVE_WAYLAND #include "wayland/meta-xwayland-private.h" #include "wayland/meta-wayland-tablet-seat.h" #include "wayland/meta-wayland-tablet-pad.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 { 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, 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 const char *gnome_wm_keybindings = "Ukwm"; static const char *net_wm_name = "Ukwm"; static void update_cursor_theme (void); 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[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, G_TYPE_UINT, 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_SCREEN, 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_SCREEN, 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 Ukwm * 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 Ukwm * 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); 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 */ if (ping_data->ping_timeout_id != 0) g_source_remove (ping_data->ping_timeout_id); 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 void enable_compositor (MetaDisplay *display) { if (!META_DISPLAY_HAS_COMPOSITE (display) || !META_DISPLAY_HAS_DAMAGE (display)) { meta_warning ("Missing %s extension required for compositing", !META_DISPLAY_HAS_COMPOSITE (display) ? "composite" : "damage"); return; } int version = (display->composite_major_version * 10) + display->composite_minor_version; if (version < 3) { meta_warning ("Your version of COMPOSITE is too old."); return; } if (!display->compositor) display->compositor = meta_compositor_new (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. */ } /** * 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 (the_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 (the_display == NULL); gnome_wm_keybindings = wm_keybindings; } 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) { if (meta_is_wayland_compositor ()) { if (state == META_SEQUENCE_ACCEPTED) meta_display_cancel_touch (display); } else { MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); int event_mode; if (state == META_SEQUENCE_ACCEPTED) event_mode = XIAcceptTouch; else if (state == META_SEQUENCE_REJECTED) event_mode = XIRejectTouch; else return; XIAllowTouchEvents (meta_backend_x11_get_xdisplay (backend), META_VIRTUAL_CORE_POINTER_ID, clutter_x11_event_sequence_get_touch_detail (sequence), DefaultRootWindow (display->xdisplay), event_mode); } } static void on_startup_notification_changed (MetaStartupNotification *sn, gpointer sequence, MetaDisplay *display) { if (!display->screen) return; g_slist_free (display->screen->startup_sequences); display->screen->startup_sequences = meta_startup_notification_get_sequences (display->startup_notification); g_signal_emit_by_name (display->screen, "startup-sequence-changed", sequence); } /** * 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) { MetaDisplay *display; Display *xdisplay; MetaScreen *screen; int i; guint32 timestamp; Window old_active_xwindow = None; /* A list of all atom names, so that we can intern them in one go. */ const char *atom_names[] = { #define item(x) #x, #include #undef item }; Atom atoms[G_N_ELEMENTS(atom_names)]; meta_verbose ("Opening display '%s'\n", XDisplayName (NULL)); xdisplay = meta_ui_get_display (); if (xdisplay == NULL) { meta_warning (_("Failed to open X Window System display “%sâ€\n"), XDisplayName (NULL)); return FALSE; } #ifdef HAVE_WAYLAND if (meta_is_wayland_compositor ()) meta_xwayland_complete_init (); #endif if (meta_is_syncing ()) XSynchronize (xdisplay, True); g_assert (the_display == NULL); display = the_display = g_object_new (META_TYPE_DISPLAY, NULL); display->closing = 0; /* here we use XDisplayName which is what the user * probably put in, vs. DisplayString(display) which is * canonicalized by XOpenDisplay() */ display->name = g_strdup (XDisplayName (NULL)); display->xdisplay = xdisplay; display->display_opening = TRUE; display->pending_pings = NULL; display->autoraise_timeout_id = 0; display->autoraise_window = NULL; display->focus_window = NULL; display->focus_serial = 0; display->server_focus_window = None; display->server_focus_serial = 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 */ meta_bell_init (display); meta_display_init_keys (display); meta_prefs_add_listener (prefs_changed_callback, display); meta_verbose ("Creating %d atoms\n", (int) G_N_ELEMENTS (atom_names)); XInternAtoms (display->xdisplay, (char **)atom_names, G_N_ELEMENTS (atom_names), False, atoms); i = 0; #define item(x) display->atom_##x = atoms[i++]; #include #undef item display->prop_hooks = NULL; meta_display_init_window_prop_hooks (display); display->group_prop_hooks = NULL; meta_display_init_group_prop_hooks (display); /* Offscreen unmapped window used for _NET_SUPPORTING_WM_CHECK, * created in screen_new */ display->leader_window = None; display->timestamp_pinging_window = None; display->groups_by_leader = NULL; display->screen = NULL; /* Get events */ meta_display_init_events (display); meta_display_init_events_x11 (display); display->xids = g_hash_table_new (meta_unsigned_long_hash, meta_unsigned_long_equal); display->stamps = g_hash_table_new (g_int64_hash, g_int64_equal); display->wayland_windows = g_hash_table_new (NULL, NULL); i = 0; while (i < N_IGNORED_CROSSING_SERIALS) { display->ignored_crossing_serials[i] = 0; ++i; } display->current_time = CurrentTime; display->sentinel_counter = 0; display->grab_resize_timeout_id = 0; display->grab_have_keyboard = FALSE; display->last_bell_time = 0; display->grab_op = META_GRAB_OP_NONE; display->grab_window = NULL; display->grab_tile_mode = META_TILE_NONE; display->grab_tile_monitor_number = -1; display->grab_edge_resistance_data = NULL; { int major, minor; display->have_xsync = FALSE; display->xsync_error_base = 0; 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 (display->xdisplay, &display->xsync_event_base, &display->xsync_error_base) || !XSyncInitialize (display->xdisplay, &major, &minor)) { display->xsync_error_base = 0; display->xsync_event_base = 0; } else { display->have_xsync = TRUE; XSyncSetPriority (display->xdisplay, None, 10); } meta_verbose ("Attempted to init Xsync, found version %d.%d error base %d event base %d\n", major, minor, display->xsync_error_base, display->xsync_event_base); } { display->have_shape = FALSE; display->shape_error_base = 0; display->shape_event_base = 0; if (!XShapeQueryExtension (display->xdisplay, &display->shape_event_base, &display->shape_error_base)) { display->shape_error_base = 0; display->shape_event_base = 0; } else display->have_shape = TRUE; meta_verbose ("Attempted to init Shape, found error base %d event base %d\n", display->shape_error_base, display->shape_event_base); } { display->have_composite = FALSE; display->composite_error_base = 0; display->composite_event_base = 0; if (!XCompositeQueryExtension (display->xdisplay, &display->composite_event_base, &display->composite_error_base)) { display->composite_error_base = 0; display->composite_event_base = 0; } else { display->composite_major_version = 0; display->composite_minor_version = 0; if (XCompositeQueryVersion (display->xdisplay, &display->composite_major_version, &display->composite_minor_version)) { display->have_composite = TRUE; } else { display->composite_major_version = 0; display->composite_minor_version = 0; } } meta_verbose ("Attempted to init Composite, found error base %d event base %d " "extn ver %d %d\n", display->composite_error_base, display->composite_event_base, display->composite_major_version, display->composite_minor_version); display->have_damage = FALSE; display->damage_error_base = 0; display->damage_event_base = 0; if (!XDamageQueryExtension (display->xdisplay, &display->damage_event_base, &display->damage_error_base)) { display->damage_error_base = 0; display->damage_event_base = 0; } else display->have_damage = TRUE; meta_verbose ("Attempted to init Damage, found error base %d event base %d\n", display->damage_error_base, display->damage_event_base); display->xfixes_error_base = 0; display->xfixes_event_base = 0; if (XFixesQueryExtension (display->xdisplay, &display->xfixes_event_base, &display->xfixes_error_base)) { int xfixes_major, xfixes_minor; XFixesQueryVersion (display->xdisplay, &xfixes_major, &xfixes_minor); if (xfixes_major * 100 + xfixes_minor < 500) meta_fatal ("Ukwm requires XFixes 5.0"); } else { meta_fatal ("Ukwm requires XFixes 5.0"); } meta_verbose ("Attempted to init XFixes, found error base %d event base %d\n", display->xfixes_error_base, display->xfixes_event_base); } { int major = 2, minor = 3; gboolean has_xi = FALSE; if (XQueryExtension (display->xdisplay, "XInputExtension", &display->xinput_opcode, &display->xinput_error_base, &display->xinput_event_base)) { if (XIQueryVersion (display->xdisplay, &major, &minor) == Success) { int version = (major * 10) + minor; if (version >= 22) has_xi = TRUE; #ifdef HAVE_XI23 if (version >= 23) display->have_xinput_23 = TRUE; #endif /* HAVE_XI23 */ } } if (!has_xi) meta_fatal ("X server doesn't have the XInput extension, version 2.2 or newer\n"); } update_cursor_theme (); /* Create the leader window here. Set its properties and * use the timestamp from one of the PropertyNotify events * that will follow. */ { 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. */ display->leader_window = meta_create_offscreen_window (display->xdisplay, DefaultRootWindow (display->xdisplay), PropertyChangeMask); meta_prop_set_utf8_string_hint (display, display->leader_window, display->atom__NET_WM_NAME, net_wm_name); meta_prop_set_utf8_string_hint (display, display->leader_window, display->atom__GNOME_WM_KEYBINDINGS, gnome_wm_keybindings); meta_prop_set_utf8_string_hint (display, display->leader_window, display->atom__UKWM_VERSION, VERSION); data[0] = display->leader_window; XChangeProperty (display->xdisplay, display->leader_window, display->atom__NET_SUPPORTING_WM_CHECK, XA_WINDOW, 32, PropModeReplace, (guchar*) data, 1); XWindowEvent (display->xdisplay, display->leader_window, PropertyChangeMask, &event); timestamp = event.xproperty.time; /* Make it painfully clear that we can't rely on PropertyNotify events on * this window, as per bug 354213. */ XSelectInput(display->xdisplay, display->leader_window, NoEventMask); } /* Make a little window used only for pinging the server for timestamps; note * that meta_create_offscreen_window already selects for PropertyChangeMask. */ display->timestamp_pinging_window = meta_create_offscreen_window (display->xdisplay, DefaultRootWindow (display->xdisplay), PropertyChangeMask); display->last_focus_time = timestamp; display->last_user_time = timestamp; display->compositor = NULL; /* Ukwm used to manage all X screens of the display in a single process, but * now it always manages exactly one screen - the default screen retrieved * from GDK. */ screen = meta_screen_new (display, timestamp); if (!screen) { /* This would typically happen because all the screens already * have window managers. */ meta_display_close (display, timestamp); return FALSE; } display->screen = screen; if (!meta_is_wayland_compositor ()) meta_prop_get_window (display, display->screen->xroot, display->atom__NET_ACTIVE_WINDOW, &old_active_xwindow); display->startup_notification = meta_startup_notification_get (display); g_signal_connect (display->startup_notification, "changed", G_CALLBACK (on_startup_notification_changed), display); meta_screen_init_workspaces (screen); enable_compositor (display); meta_screen_create_guard_window (screen); /* 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 ukwm is running as a Wayland compositor, * we start out with no windows. */ if (!meta_is_wayland_compositor ()) meta_screen_manage_all_windows (screen); if (old_active_xwindow != None) { MetaWindow *old_active_window = meta_display_lookup_x_window (display, old_active_xwindow); if (old_active_window) meta_window_focus (old_active_window, timestamp); else meta_display_focus_the_no_focus_window (display, display->screen, timestamp); } else meta_display_focus_the_no_focus_window (display, display->screen, timestamp); meta_idle_monitor_init_dbus (); /* 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): 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; g_hash_table_iter_init (&iter, 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; meta_prefs_remove_listener (prefs_changed_callback, display); meta_display_remove_autoraise_callback (display); g_clear_object (&display->startup_notification); g_clear_object (&display->gesture_tracker); if (display->focus_timeout_id) g_source_remove (display->focus_timeout_id); display->focus_timeout_id = 0; /* Stop caring about events */ meta_display_free_events_x11 (display); meta_display_free_events (display); if (display->screen) meta_screen_free (display->screen, timestamp); display->screen = NULL; /* Must be after all calls to meta_window_unmanage() since they * unregister windows */ g_hash_table_destroy (display->xids); g_hash_table_destroy (display->wayland_windows); if (display->leader_window != None) XDestroyWindow (display->xdisplay, display->leader_window); XFlush (display->xdisplay); meta_display_free_window_prop_hooks (display); meta_display_free_group_prop_hooks (display); g_free (display->name); meta_display_shutdown_keys (display); if (display->compositor) meta_compositor_destroy (display->compositor); 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->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 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; } static Bool find_timestamp_predicate (Display *xdisplay, XEvent *ev, XPointer arg) { MetaDisplay *display = (MetaDisplay *) arg; return (ev->type == PropertyNotify && ev->xproperty.atom == display->atom__UKWM_TIMESTAMP_PING); } /* Get a timestamp, even if it means a roundtrip */ guint32 meta_display_get_current_time_roundtrip (MetaDisplay *display) { guint32 timestamp; timestamp = meta_display_get_current_time (display); if (timestamp == CurrentTime) { XEvent property_event; XChangeProperty (display->xdisplay, display->timestamp_pinging_window, display->atom__UKWM_TIMESTAMP_PING, XA_STRING, 8, PropModeAppend, NULL, 0); XIfEvent (display->xdisplay, &property_event, find_timestamp_predicate, (XPointer) display); timestamp = property_event.xproperty.time; } meta_display_sanity_check_timestamps (display, timestamp); return timestamp; } /** * 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->screen->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 ()); if (display->autoraise_timeout_id != 0) g_source_remove (display->autoraise_timeout_id); 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, "[ukwm] 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)); if (!meta_display_windows_are_interactable (display)) focus_window = NULL; else if (meta_display_xwindow_is_a_no_focus_window (display, display->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, Window xwindow, gulong serial, gboolean focused_by_us) { display->focus_serial = serial; display->focused_by_us = focused_by_us; if (display->focus_xwindow == xwindow && 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; display->focus_xwindow = None; meta_window_set_focused_internal (previous, FALSE); } display->focus_window = window; display->focus_xwindow = xwindow; if (display->focus_window) { meta_topic (META_DEBUG_FOCUS, "* Focus --> %s with serial %lu\n", display->focus_window->desc, serial); meta_window_set_focused_internal (display->focus_window, TRUE); } else meta_topic (META_DEBUG_FOCUS, "* Focus --> NULL with serial %lu\n", serial); if (meta_is_wayland_compositor ()) meta_display_sync_wayland_input_focus (display); g_object_notify (G_OBJECT (display), "focus-window"); meta_display_update_active_window_hint (display); } 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 == CurrentTime) { *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; } static void request_xserver_input_focus_change (MetaDisplay *display, MetaScreen *screen, MetaWindow *meta_window, Window xwindow, guint32 timestamp) { gulong serial; if (meta_display_timestamp_too_old (display, ×tamp)) return; meta_error_trap_push (display); /* In order for ukwm 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 (display->xdisplay); serial = XNextRequest (display->xdisplay); XSetInputFocus (display->xdisplay, xwindow, RevertToPointerRoot, timestamp); XChangeProperty (display->xdisplay, display->timestamp_pinging_window, display->atom__UKWM_FOCUS_SET, XA_STRING, 8, PropModeAppend, NULL, 0); XUngrabServer (display->xdisplay); XFlush (display->xdisplay); meta_display_update_focus_window (display, meta_window, xwindow, serial, TRUE); meta_error_trap_pop (display); display->last_focus_time = timestamp; if (meta_window == NULL || meta_window != display->autoraise_window) meta_display_remove_autoraise_callback (display); } MetaWindow* meta_display_lookup_x_window (MetaDisplay *display, Window xwindow) { return g_hash_table_lookup (display->xids, &xwindow); } void meta_display_register_x_window (MetaDisplay *display, Window *xwindowp, MetaWindow *window) { g_return_if_fail (g_hash_table_lookup (display->xids, xwindowp) == NULL); g_hash_table_insert (display->xids, xwindowp, window); } void meta_display_unregister_x_window (MetaDisplay *display, Window xwindow) { g_return_if_fail (g_hash_table_lookup (display->xids, &xwindow) != NULL); g_hash_table_remove (display->xids, &xwindow); } 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)) return meta_display_lookup_x_window (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; } /* 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_display_lookup_sync_alarm (MetaDisplay *display, XSyncAlarm alarm) { return g_hash_table_lookup (display->xids, &alarm); } void meta_display_register_sync_alarm (MetaDisplay *display, XSyncAlarm *alarmp, MetaWindow *window) { g_return_if_fail (g_hash_table_lookup (display->xids, alarmp) == NULL); g_hash_table_insert (display->xids, alarmp, window); } void meta_display_unregister_sync_alarm (MetaDisplay *display, XSyncAlarm alarm) { g_return_if_fail (g_hash_table_lookup (display->xids, &alarm) != NULL); g_hash_table_remove (display->xids, &alarm); } void meta_display_notify_window_created (MetaDisplay *display, MetaWindow *window) { g_signal_emit (display, display_signals[WINDOW_CREATED], 0, window); } /** * meta_display_xwindow_is_a_no_focus_window: * @display: A #MetaDisplay * @xwindow: An X11 window * * Returns: %TRUE iff window is one of ukwm's internal "no focus" windows * (there is one per screen) which will have the focus when there is no * actual client window focused. */ gboolean meta_display_xwindow_is_a_no_focus_window (MetaDisplay *display, Window xwindow) { return xwindow == display->screen->no_focus_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; } void meta_display_update_cursor (MetaDisplay *display) { meta_screen_set_cursor (display->screen, 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 (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 (); } } gboolean meta_display_begin_grab_op (MetaDisplay *display, MetaScreen *screen, 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; /* 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->xdisplay, META_VIRTUAL_CORE_POINTER_ID, timestamp); XSync (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.tv_sec = 0; display->grab_last_moveresize_time.tv_usec = 0; display->grab_last_user_action_was_snap = FALSE; display->grab_frame_action = frame_action; meta_display_update_cursor (display); if (display->grab_resize_timeout_id) { g_source_remove (display->grab_resize_timeout_id); display->grab_resize_timeout_id = 0; } 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, screen, 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); g_signal_emit (display, display_signals[GRAB_OP_END], 0, display->screen, grab_window, grab_op); /* 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; meta_display_update_cursor (display); if (display->grab_resize_timeout_id) { g_source_remove (display->grab_resize_timeout_id); display->grab_resize_timeout_id = 0; } if (meta_is_wayland_compositor ()) meta_display_sync_wayland_input_focus (display); } /** * 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 * Ukwm doesn't currently have a grab. %META_GRAB_OP_COMPOSITOR will * be returned if a compositor-plugin modal operation is in effect * (See ukwm_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_increment_event_serial (MetaDisplay *display) { /* We just make some random X request */ XDeleteProperty (display->xdisplay, display->leader_window, display->atom__MOTIF_WM_HINTS); } void meta_display_update_active_window_hint (MetaDisplay *display) { gulong data[1]; if (display->closing) return; /* Leave old value for a replacement */ if (display->focus_window) data[0] = display->focus_window->xwindow; else data[0] = None; meta_error_trap_push (display); XChangeProperty (display->xdisplay, display->screen->xroot, display->atom__NET_ACTIVE_WINDOW, XA_WINDOW, 32, PropModeReplace, (guchar*) data, 1); meta_error_trap_pop (display); } 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); } void meta_display_retheme_all (void) { meta_display_queue_retheme_all_windows (meta_get_display ()); } static void set_cursor_theme (Display *xdisplay) { XcursorSetTheme (xdisplay, meta_prefs_get_cursor_theme ()); XcursorSetDefaultSize (xdisplay, meta_prefs_get_cursor_size ()); } static void update_cursor_theme (void) { { MetaDisplay *display = meta_get_display (); set_cursor_theme (display->xdisplay); if (display->screen) meta_screen_update_cursor (display->screen); } { MetaBackend *backend = meta_get_backend (); if (META_IS_BACKEND_X11 (backend)) { Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); set_cursor_theme (xdisplay); } } } /* * 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 Ukwm 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 ()->xdisplay, is_syncing); } } /* * How long, in milliseconds, we should wait after pinging a window * before deciding it's not going to get back to us. */ #define PING_TIMEOUT_DELAY 5000 /** * 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) { MetaDisplay *display = window->display; MetaPingData *ping_data; if (serial == 0) { meta_warning ("Tried to ping a window with a bad serial! Not allowed.\n"); return; } if (!window->can_ping) return; ping_data = g_new (MetaPingData, 1); ping_data->window = window; ping_data->serial = serial; ping_data->ping_timeout_id = g_timeout_add (PING_TIMEOUT_DELAY, meta_display_ping_timeout, ping_data); g_source_set_name_by_id (ping_data->ping_timeout_id, "[ukwm] 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 */ if (ping_data->ping_timeout_id != 0) { g_source_remove (ping_data->ping_timeout_id); ping_data->ping_timeout_id = 0; } 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 && l_window->workspace != 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; } int meta_resize_gravity_from_grab_op (MetaGrabOp op) { int gravity; gravity = -1; switch (op) { case META_GRAB_OP_RESIZING_SE: case META_GRAB_OP_KEYBOARD_RESIZING_SE: gravity = NorthWestGravity; break; case META_GRAB_OP_KEYBOARD_RESIZING_S: case META_GRAB_OP_RESIZING_S: gravity = NorthGravity; break; case META_GRAB_OP_KEYBOARD_RESIZING_SW: case META_GRAB_OP_RESIZING_SW: gravity = NorthEastGravity; break; case META_GRAB_OP_KEYBOARD_RESIZING_N: case META_GRAB_OP_RESIZING_N: gravity = SouthGravity; break; case META_GRAB_OP_KEYBOARD_RESIZING_NE: case META_GRAB_OP_RESIZING_NE: gravity = SouthWestGravity; break; case META_GRAB_OP_KEYBOARD_RESIZING_NW: case META_GRAB_OP_RESIZING_NW: gravity = SouthEastGravity; break; case META_GRAB_OP_KEYBOARD_RESIZING_E: case META_GRAB_OP_RESIZING_E: gravity = WestGravity; break; case META_GRAB_OP_KEYBOARD_RESIZING_W: case META_GRAB_OP_RESIZING_W: gravity = EastGravity; break; case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN: gravity = CenterGravity; break; default: break; } return gravity; } void meta_display_unmanage_screen (MetaDisplay *display, MetaScreen *screen, guint32 timestamp) { meta_verbose ("Unmanaging screen %d on display %s\n", meta_ui_get_screen_number (), display->name); meta_display_close (display, timestamp); } void meta_display_unmanage_windows_for_screen (MetaDisplay *display, MetaScreen *screen, 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->screen->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_AUDIBLE_BELL) { meta_bell_set_audible (display, meta_prefs_bell_is_audible ()); } else if (pref == META_PREF_CURSOR_THEME || pref == META_PREF_CURSOR_SIZE) { update_cursor_theme (); } } void meta_display_increment_focus_sentinel (MetaDisplay *display) { unsigned long data[1]; data[0] = meta_display_get_current_time (display); XChangeProperty (display->xdisplay, display->screen->xroot, display->atom__UKWM_SENTINEL, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 1); display->sentinel_counter += 1; } void meta_display_decrement_focus_sentinel (MetaDisplay *display) { display->sentinel_counter -= 1; if (display->sentinel_counter < 0) display->sentinel_counter = 0; } gboolean meta_display_focus_sentinel_clear (MetaDisplay *display) { return (display->sentinel_counter == 0); } 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_set_input_focus_window (MetaDisplay *display, MetaWindow *window, gboolean focus_frame, guint32 timestamp) { request_xserver_input_focus_change (display, window->screen, window, focus_frame ? window->frame->xwindow : window->xwindow, timestamp); } void meta_display_set_input_focus_xwindow (MetaDisplay *display, MetaScreen *screen, Window window, guint32 timestamp) { request_xserver_input_focus_change (display, screen, NULL, window, timestamp); } void meta_display_focus_the_no_focus_window (MetaDisplay *display, MetaScreen *screen, guint32 timestamp) { request_xserver_input_focus_change (display, screen, NULL, screen->no_focus_window, timestamp); } void meta_display_remove_autoraise_callback (MetaDisplay *display) { if (display->autoraise_timeout_id != 0) { g_source_remove (display->autoraise_timeout_id); display->autoraise_timeout_id = 0; 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_input_device_get_device_id (event->device), 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_get_xinput_opcode: (skip) * @display: a #MetaDisplay * */ int meta_display_get_xinput_opcode (MetaDisplay *display) { return display->xinput_opcode; } /** * 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 (meta_get_backend ())) { return (META_DISPLAY_HAS_XINPUT_23 (display) && !meta_is_wayland_compositor()); } g_assert_not_reached (); } /** * meta_display_get_xdisplay: (skip) * @display: a #MetaDisplay * */ Display * meta_display_get_xdisplay (MetaDisplay *display) { return display->xdisplay; } /** * meta_display_get_compositor: (skip) * @display: a #MetaDisplay * */ MetaCompositor * meta_display_get_compositor (MetaDisplay *display) { return display->compositor; } gboolean meta_display_has_shape (MetaDisplay *display) { return META_DISPLAY_HAS_SHAPE (display); } /** * 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; } int meta_display_get_damage_event_base (MetaDisplay *display) { return display->damage_event_base; } int meta_display_get_shape_event_base (MetaDisplay *display) { return display->shape_event_base; } /** * 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; } Cursor meta_display_create_x_cursor (MetaDisplay *display, MetaCursor cursor) { return meta_cursor_create_x_cursor (display->xdisplay, cursor); } 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_set_alarm_filter (MetaDisplay *display, MetaAlarmFilter filter, gpointer data) { g_return_if_fail (filter == NULL || display->alarm_filter == NULL); display->alarm_filter = filter; display->alarm_filter_data = data; } 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_settings_get_tablet_wacom_device (input_settings, 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_screen_get_monitor_index_for_rect (display->screen, &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); } ukwm/src/core/frame.c0000664000175000017500000002773713220600404013447 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm X window decorations */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2003, 2004 Red Hat, Inc. * Copyright (C) 2005 Elijah Newren * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include #include "frame.h" #include "bell.h" #include #include "keybindings-private.h" #include "backends/x11/meta-backend-x11.h" #define EVENT_MASK (SubstructureRedirectMask | \ StructureNotifyMask | SubstructureNotifyMask | \ ExposureMask | FocusChangeMask) void meta_window_ensure_frame (MetaWindow *window) { MetaFrame *frame; XSetWindowAttributes attrs; gulong create_serial; if (window->frame) return; 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->is_flashing = FALSE; 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 (window->screen->ui, window->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->screen->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 (window->display->xdisplay, frame->xwindow, CWEventMask, &attrs); meta_display_register_x_window (window->display, &frame->xwindow, window); meta_error_trap_push (window->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->screen->stack_tracker, window->xwindow, XNextRequest (window->display->xdisplay)); XReparentWindow (window->display->xdisplay, window->xwindow, frame->xwindow, frame->child_x, frame->child_y); /* FIXME handle this error */ meta_error_trap_pop (window->display); /* 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 (frame->window->screen->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 (window->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; if (window->frame == NULL) return; meta_verbose ("Unframing window %s\n", window->desc); frame = window->frame; meta_frame_calc_borders (frame, &borders); meta_bell_notify_frame_destroy (frame); /* Unparent the client window; it may be destroyed, * thus the error trap. */ meta_error_trap_push (window->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; } meta_stack_tracker_record_add (window->screen->stack_tracker, window->xwindow, XNextRequest (window->display->xdisplay)); XReparentWindow (window->display->xdisplay, window->xwindow, window->screen->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); meta_error_trap_pop (window->display); meta_ui_frame_unmanage (frame->ui_frame); meta_display_unregister_x_window (window->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 (meta_prefs_get_show_fallback_app_menu () && frame->window->gtk_app_menu_object_path) flags |= META_FRAME_ALLOWS_APPMENU; 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_LEFT (frame->window)) flags |= META_FRAME_TILED_LEFT; if (META_WINDOW_TILED_RIGHT (frame->window)) flags |= META_FRAME_TILED_RIGHT; if (frame->window->fullscreen) flags |= META_FRAME_FULLSCREEN; if (frame->is_flashing) flags |= META_FRAME_IS_FLASHING; 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_t *cr) { meta_ui_frame_get_mask (frame->ui_frame, 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) { Cursor xcursor; if (cursor == frame->current_cursor) return; frame->current_cursor = cursor; if (cursor == META_CURSOR_DEFAULT) XUndefineCursor (frame->window->display->xdisplay, frame->xwindow); else { xcursor = meta_display_create_x_cursor (frame->window->display, cursor); XDefineCursor (frame->window->display->xdisplay, frame->xwindow, xcursor); XFlush (frame->window->display->xdisplay); XFreeCursor (frame->window->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); } ukwm/src/core/meta-inhibit-shortcuts-dialog.c0000664000175000017500000000646713220600404020215 0ustar fengfeng/* * 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); } ukwm/src/core/stack-tracker.c0000664000175000017500000010235613220600404015102 0ustar fengfeng/* -*- 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 #include #include "frame.h" #include "screen-private.h" #include "stack-tracker.h" #include #include #include /* 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 { MetaScreen *screen; /* 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->screen->display, window); } static void meta_stack_op_dump (MetaStackTracker *tracker, MetaStackOp *op, const char *prefix, const char *suffix) { const char *window_desc = get_window_desc (tracker, op->any.window); 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; } } } 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 (); } static void meta_stack_tracker_dump (MetaStackTracker *tracker) { 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 (); } 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 (MetaStackTracker *tracker) { MetaScreen *screen = tracker->screen; Window ignored1, ignored2; Window *children; guint n_children; guint i; tracker->xserver_serial = XNextRequest (screen->display->xdisplay); XQueryTree (screen->display->xdisplay, screen->xroot, &ignored1, &ignored2, &children, &n_children); tracker->verified_stack = g_array_sized_new (FALSE, FALSE, sizeof (guint64), n_children); g_array_set_size (tracker->verified_stack, n_children); for (i = 0; i < n_children; i++) g_array_index (tracker->verified_stack, guint64, i) = children[i]; XFree (children); } MetaStackTracker * meta_stack_tracker_new (MetaScreen *screen) { MetaStackTracker *tracker; tracker = g_new0 (MetaStackTracker, 1); tracker->screen = screen; query_xserver_stack (tracker); tracker->unverified_predictions = g_queue_new (); 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_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); } /** * 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)) { MetaWindow *meta_window = meta_display_lookup_x_window (tracker->screen->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->screen->display, window)); } meta_compositor_sync_stack (tracker->screen->display->compositor, meta_windows); g_list_free (meta_windows); meta_screen_restacked (tracker->screen); } 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; 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 (tracker->screen->display->xdisplay); meta_error_trap_push (tracker->screen->display); changes.stack_mode = changes.sibling ? Below : Above; XConfigureWindow (tracker->screen->display->xdisplay, window, (changes.sibling ? CWSibling : 0) | CWStackMode, &changes); meta_error_trap_pop (tracker->screen->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; 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 (tracker->screen->display->xdisplay); meta_error_trap_push (tracker->screen->display); changes.stack_mode = changes.sibling ? Above : Below; XConfigureWindow (tracker->screen->display->xdisplay, (Window)window, (changes.sibling ? CWSibling : 0) | CWStackMode, &changes); meta_error_trap_pop (tracker->screen->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) { MetaWindow *window; 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--) { window = meta_display_lookup_stack_id (tracker->screen->display, stack[i]); if (window && window->layer != META_LAYER_OVERRIDE_REDIRECT) break; } topmost_non_or = i; for (i -= 1; i >= 0; i--) { window = meta_display_lookup_stack_id (tracker->screen->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->screen->display, windows[old_pos]); if ((old_window && !old_window->override_redirect && !old_window->unmanaging) || windows[old_pos] == tracker->screen->guard_window) 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 (windows[old_pos] == tracker->screen->guard_window) break; if (windows[old_pos] == managed[new_pos]) { old_pos--; new_pos--; continue; } MetaWindow *old_window = meta_display_lookup_stack_id (tracker->screen->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); } } } ukwm/src/core/meta-inhibit-shortcuts-dialog-default.c0000664000175000017500000001025513220600404021625 0ustar fengfeng/* * 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 #include "util-private.h" #include "window-private.h" #include "meta/meta-inhibit-shortcuts-dialog.h" #include "meta-inhibit-shortcuts-dialog-default-private.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); } ukwm/src/core/window-private.h0000664000175000017500000007156213220600404015334 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * \file window-private.h Windows which Ukwm 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 * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_WINDOW_PRIVATE_H #define META_WINDOW_PRIVATE_H #include #include #include #include #include "screen-private.h" #include #include "stack.h" #include #include #include #include #include "x11/group-private.h" #include "wayland/meta-wayland-types.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_RESIZE = 1 << 4, META_MOVE_RESIZE_STATE_CHANGED = 1 << 5, } 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, } 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 struct _MetaPlacementRule { MetaRectangle anchor_rect; MetaPlacementGravity gravity; MetaPlacementAnchor anchor; MetaPlacementConstraintAdjustment constraint_adjustment; int offset_x; int offset_y; int width; int height; } MetaPlacementRule; 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; MetaScreen *screen; 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; 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 *ukwm_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 hide_titlebar_when_maximized; 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; /* The current tile mode */ guint tile_mode : 2; /* The last "full" maximized/unmaximized state. We need to keep track of * that to toggle between normal/tiled or maximized/tiled states. */ guint saved_maximize : 1; int tile_monitor_number; /* 0 - top * 1 - right * 2 - bottom * 3 - left */ MetaEdgeConstraint edge_constraints[4]; double tile_hfraction; int 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; /* These are the flags from WM_PROTOCOLS */ guint take_focus : 1; guint delete_window : 1; guint can_ping : 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; /* 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; /* 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 window, if any */ MetaWindow *tile_match; /* Bypass compositor hints */ guint bypass_compositor; MetaPlacementRule *placement_rule; }; 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, int gravity, MetaRectangle unconstrained_rect, MetaRectangle constrained_rect, 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, gboolean user_op); void (*main_monitor_changed) (MetaWindow *window, const MetaLogicalMonitor *old); void (*force_restore_shortcuts) (MetaWindow *window, ClutterInputDevice *source); gboolean (*shortcuts_inhibited) (MetaWindow *window, ClutterInputDevice *source); }; /* 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_SIDE_BY_SIDE(w) ((w)->maximized_vertically && \ !(w)->maximized_horizontally && \ (w)->tile_mode != META_TILE_NONE) #define META_WINDOW_TILED_LEFT(w) (META_WINDOW_TILED_SIDE_BY_SIDE(w) && \ (w)->tile_mode == META_TILE_LEFT) #define META_WINDOW_TILED_RIGHT(w) (META_WINDOW_TILED_SIDE_BY_SIDE(w) && \ (w)->tile_mode == META_TILE_RIGHT) #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, MetaScreen *screen, 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_queue (MetaWindow *window, guint queuebits); void meta_window_tile (MetaWindow *window, MetaTileMode mode); 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_resize_frame_with_gravity (MetaWindow *window, gboolean user_op, int w, int h, int 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, int 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); 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) \ (((w)->input || (w)->take_focus ) && META_WINDOW_IN_NORMAL_TAB_CHAIN_TYPE (w) && (!(w)->skip_taskbar)) #define META_WINDOW_IN_DOCK_TAB_CHAIN(w) \ (((w)->input || (w)->take_focus) && (! META_WINDOW_IN_NORMAL_TAB_CHAIN_TYPE (w) || (w)->skip_taskbar)) #define META_WINDOW_IN_GROUP_TAB_CHAIN(w, g) \ (((w)->input || (w)->take_focus) && (!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); 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); 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_side_by_side (MetaWindow *window); void meta_window_compute_tile_match (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); void meta_window_set_transient_for (MetaWindow *window, MetaWindow *parent); void meta_window_set_opacity (MetaWindow *window, guint8 opacity); void meta_window_handle_enter (MetaWindow *window, guint32 timestamp, guint root_x, guint root_y); void meta_window_handle_leave (MetaWindow *window); void meta_window_handle_ungrabbed_event (MetaWindow *window, const ClutterEvent *event); uint32_t meta_window_get_client_pid (MetaWindow *window); 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, gboolean user_op); 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, int 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); void meta_window_force_restore_shortcuts (MetaWindow *window, ClutterInputDevice *source); gboolean meta_window_shortcuts_inhibited (MetaWindow *window, ClutterInputDevice *source); #endif ukwm/src/core/screen.c0000664000175000017500000023756613220600404013637 0ustar fengfeng/* -*- 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) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ /** * SECTION:screen * @title: MetaScreen * @short_description: Ukwm X screen handler */ #include #include "screen-private.h" #include #include "util-private.h" #include #include "window-private.h" #include "frame.h" #include #include "workspace-private.h" #include "keybindings-private.h" #include "stack.h" #include #include #include "core.h" #include "meta-cursor-tracker-private.h" #include "boxes-private.h" #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include #include #include #include #include #include #include #include "x11/window-x11.h" #include "x11/xprops.h" #include "backends/x11/meta-backend-x11.h" static char* get_screen_name (MetaDisplay *display, int number); static void update_num_workspaces (MetaScreen *screen, guint32 timestamp); static void set_workspace_names (MetaScreen *screen); static void prefs_changed_callback (MetaPreference pref, gpointer data); static void set_desktop_geometry_hint (MetaScreen *screen); static void set_desktop_viewport_hint (MetaScreen *screen); static void on_monitors_changed_internal (MetaMonitorManager *manager, MetaScreen *screen); static void on_monitors_changed (MetaMonitorManager *manager, MetaScreen *screen); enum { PROP_N_WORKSPACES = 1, }; enum { RESTACKED, WORKSPACE_ADDED, WORKSPACE_REMOVED, WORKSPACE_SWITCHED, WINDOW_ENTERED_MONITOR, WINDOW_LEFT_MONITOR, STARTUP_SEQUENCE_CHANGED, WORKAREAS_CHANGED, MONITORS_CHANGED, IN_FULLSCREEN_CHANGED, LAST_SIGNAL }; static guint screen_signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE (MetaScreen, meta_screen, G_TYPE_OBJECT); static GQuark quark_screen_x11_logical_monitor_data = 0; typedef struct _MetaScreenX11LogicalMonitorData { int xinerama_index; } MetaScreenX11LogicalMonitorData; static void meta_screen_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { #if 0 MetaScreen *screen = META_SCREEN (object); #endif switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_screen_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaScreen *screen = META_SCREEN (object); switch (prop_id) { case PROP_N_WORKSPACES: g_value_set_int (value, meta_screen_get_n_workspaces (screen)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_screen_finalize (GObject *object) { /* Actual freeing done in meta_screen_free() for now */ } static void meta_screen_class_init (MetaScreenClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; object_class->get_property = meta_screen_get_property; object_class->set_property = meta_screen_set_property; object_class->finalize = meta_screen_finalize; screen_signals[RESTACKED] = g_signal_new ("restacked", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MetaScreenClass, restacked), NULL, NULL, NULL, G_TYPE_NONE, 0); pspec = g_param_spec_int ("n-workspaces", "N Workspaces", "Number of workspaces", 1, G_MAXINT, 1, G_PARAM_READABLE); screen_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); screen_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); screen_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); screen_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); screen_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); screen_signals[STARTUP_SEQUENCE_CHANGED] = g_signal_new ("startup-sequence-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_POINTER); screen_signals[WORKAREAS_CHANGED] = g_signal_new ("workareas-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MetaScreenClass, workareas_changed), NULL, NULL, NULL, G_TYPE_NONE, 0); screen_signals[MONITORS_CHANGED] = g_signal_new ("monitors-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MetaScreenClass, monitors_changed), NULL, NULL, NULL, G_TYPE_NONE, 0); screen_signals[IN_FULLSCREEN_CHANGED] = g_signal_new ("in-fullscreen-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); g_object_class_install_property (object_class, PROP_N_WORKSPACES, pspec); quark_screen_x11_logical_monitor_data = g_quark_from_static_string ("-meta-screen-logical-monitor-x11-data"); } static void meta_screen_init (MetaScreen *screen) { } static int set_wm_check_hint (MetaScreen *screen) { unsigned long data[1]; g_return_val_if_fail (screen->display->leader_window != None, 0); data[0] = screen->display->leader_window; XChangeProperty (screen->display->xdisplay, screen->xroot, screen->display->atom__NET_SUPPORTING_WM_CHECK, XA_WINDOW, 32, PropModeReplace, (guchar*) data, 1); return Success; } static void unset_wm_check_hint (MetaScreen *screen) { XDeleteProperty (screen->display->xdisplay, screen->xroot, screen->display->atom__NET_SUPPORTING_WM_CHECK); } static int set_supported_hint (MetaScreen *screen) { Atom atoms[] = { #define EWMH_ATOMS_ONLY #define item(x) screen->display->atom_##x, #include #undef item #undef EWMH_ATOMS_ONLY screen->display->atom__GTK_FRAME_EXTENTS, screen->display->atom__GTK_SHOW_WINDOW_MENU, screen->display->atom__GTK_EDGE_CONSTRAINTS, }; XChangeProperty (screen->display->xdisplay, screen->xroot, screen->display->atom__NET_SUPPORTED, XA_ATOM, 32, PropModeReplace, (guchar*) atoms, G_N_ELEMENTS(atoms)); return Success; } static int set_wm_icon_size_hint (MetaScreen *screen) { #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 (screen->display->xdisplay, screen->xroot, screen->display->atom_WM_ICON_SIZE, XA_CARDINAL, 32, PropModeReplace, (guchar*) vals, N_VALS); return Success; #undef N_VALS } static MetaScreenX11LogicalMonitorData * get_screen_x11_logical_monitor_data (MetaLogicalMonitor *logical_monitor) { return g_object_get_qdata (G_OBJECT (logical_monitor), quark_screen_x11_logical_monitor_data); } static MetaScreenX11LogicalMonitorData * ensure_screen_x11_logical_monitor_data (MetaLogicalMonitor *logical_monitor) { MetaScreenX11LogicalMonitorData *data; data = get_screen_x11_logical_monitor_data (logical_monitor); if (data) return data; data = g_new0 (MetaScreenX11LogicalMonitorData, 1); g_object_set_qdata_full (G_OBJECT (logical_monitor), quark_screen_x11_logical_monitor_data, data, g_free); return data; } static void meta_screen_ensure_xinerama_indices (MetaScreen *screen) { 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 (screen->has_xinerama_indices) return; screen->has_xinerama_indices = TRUE; if (!XineramaIsActive (screen->display->xdisplay)) return; infos = XineramaQueryScreens (screen->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) { MetaScreenX11LogicalMonitorData *logical_monitor_data; logical_monitor_data = ensure_screen_x11_logical_monitor_data (logical_monitor); logical_monitor_data->xinerama_index = j; } } } meta_XFree (infos); } int meta_screen_logical_monitor_to_xinerama_index (MetaScreen *screen, MetaLogicalMonitor *logical_monitor) { MetaScreenX11LogicalMonitorData *logical_monitor_data; g_return_val_if_fail (logical_monitor, -1); meta_screen_ensure_xinerama_indices (screen); logical_monitor_data = get_screen_x11_logical_monitor_data (logical_monitor); return logical_monitor_data->xinerama_index; } MetaLogicalMonitor * meta_screen_xinerama_index_to_logical_monitor (MetaScreen *screen, int xinerama_index) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); GList *logical_monitors, *l; meta_screen_ensure_xinerama_indices (screen); logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; MetaScreenX11LogicalMonitorData *logical_monitor_data; logical_monitor_data = ensure_screen_x11_logical_monitor_data (logical_monitor); if (logical_monitor_data->xinerama_index == xinerama_index) return logical_monitor; } return NULL; } static void reload_logical_monitors (MetaScreen *screen) { GList *l; for (l = screen->workspaces; l != NULL; l = l->next) { MetaWorkspace *space = l->data; meta_workspace_invalidate_work_area (space); } screen->has_xinerama_indices = FALSE; } /* 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 (Display *xdisplay, MetaScreen *screen) { XSetWindowAttributes attributes; Window guard_window; gulong create_serial; 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(xdisplay); guard_window = XCreateWindow (xdisplay, screen->xroot, 0, /* x */ 0, /* y */ screen->rect.width, screen->rect.height, 0, /* border width */ 0, /* depth */ InputOnly, /* class */ CopyFromParent, /* visual */ CWEventMask|CWOverrideRedirect, &attributes); /* https://bugzilla.gnome.org/show_bug.cgi?id=710346 */ XStoreName (xdisplay, guard_window, "ukwm 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 (xdisplay, False); XISelectEvents (backend_xdisplay, guard_window, &mask, 1); } } meta_stack_tracker_record_add (screen->stack_tracker, guard_window, create_serial); meta_stack_tracker_lower (screen->stack_tracker, guard_window); XMapWindow (xdisplay, guard_window); return guard_window; } static Window take_manager_selection (MetaDisplay *display, Window xroot, Atom manager_atom, int timestamp, gboolean should_replace) { Display *xdisplay = display->xdisplay; Window current_owner, new_owner; current_owner = XGetSelectionOwner (xdisplay, manager_atom); if (current_owner != None) { XSetWindowAttributes attrs; if (should_replace) { /* We want to find out when the current selection owner dies */ meta_error_trap_push (display); attrs.event_mask = StructureNotifyMask; XChangeWindowAttributes (xdisplay, current_owner, CWEventMask, &attrs); if (meta_error_trap_pop_with_return (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."), 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_create_offscreen_window (xdisplay, xroot, NoEventMask); XSetSelectionOwner (xdisplay, manager_atom, new_owner, timestamp); if (XGetSelectionOwner (xdisplay, manager_atom) != new_owner) { meta_warning ("Could not acquire selection: %s", XGetAtomName (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 = display->atom_MANAGER; ev.format = 32; ev.data.l[0] = timestamp; ev.data.l[1] = manager_atom; XSendEvent (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 (xdisplay, current_owner, StructureNotifyMask, &event); while (event.type != DestroyNotify); } return new_owner; } MetaScreen* meta_screen_new (MetaDisplay *display, guint32 timestamp) { MetaScreen *screen; int number; Screen *xscreen; Window xroot; Display *xdisplay; Window new_wm_sn_owner; gboolean replace_current_wm; Atom wm_sn_atom; char buf[128]; MetaMonitorManager *manager; replace_current_wm = meta_get_replace_current_wm (); number = meta_ui_get_screen_number (); /* Only display->name, display->xdisplay, and display->error_traps * can really be used in this function, since normally screens are * created from the MetaDisplay constructor */ xdisplay = display->xdisplay; meta_verbose ("Trying screen %d on display '%s'\n", number, display->name); 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, display->name); return NULL; } sprintf (buf, "WM_S%d", number); wm_sn_atom = XInternAtom (xdisplay, buf, False); new_wm_sn_owner = take_manager_selection (display, xroot, wm_sn_atom, timestamp, replace_current_wm); if (new_wm_sn_owner == None) return NULL; { 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); #ifdef HAVE_XI23 if (META_DISPLAY_HAS_XINPUT_23 (display)) { XISetMask (mask.mask, XI_BarrierHit); XISetMask (mask.mask, XI_BarrierLeave); } #endif /* HAVE_XI23 */ XISelectEvents (xdisplay, xroot, &mask, 1); event_mask = (SubstructureRedirectMask | SubstructureNotifyMask | StructureNotifyMask | ColormapChangeMask | PropertyChangeMask); XSelectInput (xdisplay, xroot, event_mask); } /* Select for cursor changes so the cursor tracker is up to date. */ XFixesSelectCursorInput (xdisplay, xroot, XFixesDisplayCursorNotifyMask); screen = g_object_new (META_TYPE_SCREEN, NULL); screen->closing = 0; screen->display = display; screen->screen_name = get_screen_name (display, number); screen->xroot = xroot; screen->rect.x = screen->rect.y = 0; manager = meta_monitor_manager_get (); g_signal_connect (manager, "monitors-changed-internal", G_CALLBACK (on_monitors_changed_internal), screen); g_signal_connect (manager, "monitors-changed", G_CALLBACK (on_monitors_changed), screen); meta_monitor_manager_get_screen_size (manager, &screen->rect.width, &screen->rect.height); xscreen = ScreenOfDisplay (xdisplay, number); screen->current_cursor = -1; /* invalid/unset */ screen->default_xvisual = DefaultVisualOfScreen (xscreen); screen->default_depth = DefaultDepthOfScreen (xscreen); screen->wm_sn_selection_window = new_wm_sn_owner; screen->wm_sn_atom = wm_sn_atom; screen->wm_sn_timestamp = timestamp; screen->work_area_later = 0; screen->check_fullscreen_later = 0; screen->active_workspace = NULL; screen->workspaces = NULL; screen->rows_of_workspaces = 1; screen->columns_of_workspaces = -1; screen->vertical_workspaces = FALSE; screen->starting_corner = META_SCREEN_TOPLEFT; screen->guard_window = None; /* If we're a Wayland compositor, then we don't grab the COW, since it * will map it. */ if (!meta_is_wayland_compositor ()) screen->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 */ meta_restart_finish (); reload_logical_monitors (screen); meta_screen_set_cursor (screen, META_CURSOR_DEFAULT); /* Handle creating a no_focus_window for this screen */ screen->no_focus_window = meta_create_offscreen_window (display->xdisplay, screen->xroot, FocusChangeMask|KeyPressMask|KeyReleaseMask); XMapWindow (display->xdisplay, screen->no_focus_window); /* Done with no_focus_window stuff */ set_wm_icon_size_hint (screen); set_supported_hint (screen); set_wm_check_hint (screen); set_desktop_viewport_hint (screen); set_desktop_geometry_hint (screen); meta_screen_update_workspace_layout (screen); /* Screens must have at least one workspace at all times, * so create that required workspace. */ meta_workspace_new (screen); screen->keys_grabbed = FALSE; meta_screen_grab_keys (screen); screen->ui = meta_ui_new (screen->display->xdisplay); screen->tile_preview_timeout_id = 0; screen->stack = meta_stack_new (screen); screen->stack_tracker = meta_stack_tracker_new (screen); meta_prefs_add_listener (prefs_changed_callback, screen); meta_verbose ("Added screen %d ('%s') root 0x%lx\n", number, screen->screen_name, screen->xroot); return screen; } void meta_screen_init_workspaces (MetaScreen *screen) { MetaWorkspace *current_workspace; uint32_t current_workspace_index = 0; guint32 timestamp; g_return_if_fail (META_IS_SCREEN (screen)); timestamp = screen->wm_sn_timestamp; /* Get current workspace */ if (meta_prop_get_cardinal (screen->display, screen->xroot, screen->display->atom__NET_CURRENT_DESKTOP, ¤t_workspace_index)) meta_verbose ("Read existing _NET_CURRENT_DESKTOP = %d\n", (int) current_workspace_index); else meta_verbose ("No _NET_CURRENT_DESKTOP present\n"); update_num_workspaces (screen, timestamp); set_workspace_names (screen); /* Switch to the _NET_CURRENT_DESKTOP workspace */ current_workspace = meta_screen_get_workspace_by_index (screen, current_workspace_index); if (current_workspace != NULL) meta_workspace_activate (current_workspace, timestamp); else meta_workspace_activate (screen->workspaces->data, timestamp); } void meta_screen_free (MetaScreen *screen, guint32 timestamp) { MetaDisplay *display; display = screen->display; screen->closing += 1; meta_compositor_unmanage (screen->display->compositor); meta_display_unmanage_windows_for_screen (display, screen, timestamp); meta_prefs_remove_listener (prefs_changed_callback, screen); meta_screen_ungrab_keys (screen); meta_ui_free (screen->ui); meta_stack_free (screen->stack); meta_stack_tracker_free (screen->stack_tracker); meta_error_trap_push (screen->display); XSelectInput (screen->display->xdisplay, screen->xroot, 0); if (meta_error_trap_pop_with_return (screen->display) != Success) meta_warning ("Could not release screen %d on display \"%s\"\n", meta_ui_get_screen_number (), screen->display->name); unset_wm_check_hint (screen); XDestroyWindow (screen->display->xdisplay, screen->wm_sn_selection_window); if (screen->work_area_later != 0) meta_later_remove (screen->work_area_later); if (screen->check_fullscreen_later != 0) meta_later_remove (screen->check_fullscreen_later); if (screen->tile_preview_timeout_id) g_source_remove (screen->tile_preview_timeout_id); g_free (screen->screen_name); g_object_unref (screen); } void meta_screen_create_guard_window (MetaScreen *screen) { if (screen->guard_window == None) screen->guard_window = create_guard_window (screen->display->xdisplay, screen); } void meta_screen_manage_all_windows (MetaScreen *screen) { guint64 *_children; guint64 *children; int n_children, i; meta_stack_freeze (screen->stack); meta_stack_tracker_get_stack (screen->stack_tracker, &_children, &n_children); /* Copy the stack as it will be modified as part of the loop */ children = g_memdup (_children, sizeof (guint64) * n_children); for (i = 0; i < n_children; ++i) { g_assert (META_STACK_ID_IS_X11 (children[i])); meta_window_x11_new (screen->display, children[i], TRUE, META_COMP_EFFECT_NONE); } g_free (children); meta_stack_thaw (screen->stack); } static void prefs_changed_callback (MetaPreference pref, gpointer data) { MetaScreen *screen = data; if ((pref == META_PREF_NUM_WORKSPACES || pref == META_PREF_DYNAMIC_WORKSPACES) && !meta_prefs_get_dynamic_workspaces ()) { /* GSettings doesn't provide timestamps, but luckily update_num_workspaces * often doesn't need it... */ guint32 timestamp = meta_display_get_current_time_roundtrip (screen->display); update_num_workspaces (screen, timestamp); } else if (pref == META_PREF_WORKSPACE_NAMES) { set_workspace_names (screen); } } static char* get_screen_name (MetaDisplay *display, 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 (display->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_screen_foreach_window (MetaScreen *screen, MetaListWindowsFlags flags, MetaScreenWindowFunc 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 (screen->display, flags); g_slist_foreach (windows, (GFunc) func, data); g_slist_free (windows); } int meta_screen_get_n_workspaces (MetaScreen *screen) { return g_list_length (screen->workspaces); } /** * meta_screen_get_workspace_by_index: * @screen: a #MetaScreen * @index: index of one of the screen's workspaces * * Gets the workspace object for one of a screen'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): the workspace object with specified index, or %NULL * if the index is out of range. */ MetaWorkspace* meta_screen_get_workspace_by_index (MetaScreen *screen, int idx) { return g_list_nth_data (screen->workspaces, idx); } static void set_number_of_spaces_hint (MetaScreen *screen, int n_spaces) { unsigned long data[1]; if (screen->closing > 0) return; data[0] = n_spaces; meta_verbose ("Setting _NET_NUMBER_OF_DESKTOPS to %lu\n", data[0]); meta_error_trap_push (screen->display); XChangeProperty (screen->display->xdisplay, screen->xroot, screen->display->atom__NET_NUMBER_OF_DESKTOPS, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 1); meta_error_trap_pop (screen->display); } static void set_desktop_geometry_hint (MetaScreen *screen) { unsigned long data[2]; if (screen->closing > 0) return; data[0] = screen->rect.width; data[1] = screen->rect.height; meta_verbose ("Setting _NET_DESKTOP_GEOMETRY to %lu, %lu\n", data[0], data[1]); meta_error_trap_push (screen->display); XChangeProperty (screen->display->xdisplay, screen->xroot, screen->display->atom__NET_DESKTOP_GEOMETRY, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 2); meta_error_trap_pop (screen->display); } static void set_desktop_viewport_hint (MetaScreen *screen) { unsigned long data[2]; if (screen->closing > 0) return; /* * Ukwm 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_error_trap_push (screen->display); XChangeProperty (screen->display->xdisplay, screen->xroot, screen->display->atom__NET_DESKTOP_VIEWPORT, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 2); meta_error_trap_pop (screen->display); } void meta_screen_remove_workspace (MetaScreen *screen, MetaWorkspace *workspace, guint32 timestamp) { GList *l; GList *next; MetaWorkspace *neighbour = NULL; int index; gboolean active_index_changed; int new_num; l = g_list_find (screen->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 == screen->active_workspace) meta_workspace_activate (neighbour, timestamp); /* To emit the signal after removing the workspace */ index = meta_workspace_index (workspace); active_index_changed = index < meta_screen_get_active_workspace_index (screen); /* This also removes the workspace from the screens list */ meta_workspace_remove (workspace); new_num = g_list_length (screen->workspaces); set_number_of_spaces_hint (screen, new_num); 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) meta_screen_set_active_workspace_hint (screen); for (l = next; l != NULL; l = l->next) { MetaWorkspace *w = l->data; meta_workspace_index_changed (w); } meta_screen_queue_workarea_recalc (screen); g_signal_emit (screen, screen_signals[WORKSPACE_REMOVED], 0, index); g_object_notify (G_OBJECT (screen), "n-workspaces"); } /** * meta_screen_append_new_workspace: * @screen: a #MetaScreen * @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 screen and (optionally) switch to that * screen. * * Return value: (transfer none): the newly appended workspace. */ MetaWorkspace * meta_screen_append_new_workspace (MetaScreen *screen, gboolean activate, guint32 timestamp) { MetaWorkspace *w; int new_num; /* This also adds the workspace to the screen list */ w = meta_workspace_new (screen); if (!w) return NULL; if (activate) meta_workspace_activate (w, timestamp); new_num = g_list_length (screen->workspaces); set_number_of_spaces_hint (screen, new_num); if (!meta_prefs_get_dynamic_workspaces ()) meta_prefs_set_num_workspaces (new_num); meta_screen_queue_workarea_recalc (screen); g_signal_emit (screen, screen_signals[WORKSPACE_ADDED], 0, meta_workspace_index (w)); g_object_notify (G_OBJECT (screen), "n-workspaces"); return w; } static void update_num_workspaces (MetaScreen *screen, guint32 timestamp) { int new_num, old_num; GList *l; int i; GList *extras; MetaWorkspace *last_remaining; gboolean need_change_space; if (meta_prefs_get_dynamic_workspaces ()) { int n_items; uint32_t *list; n_items = 0; list = NULL; if (meta_prop_get_cardinal_list (screen->display, screen->xroot, screen->display->atom__NET_NUMBER_OF_DESKTOPS, &list, &n_items)) { new_num = list[0]; meta_XFree (list); } else { new_num = 1; } } else { new_num = meta_prefs_get_num_workspaces (); } g_assert (new_num > 0); if (g_list_length (screen->workspaces) == (guint) new_num) { if (screen->display->display_opening) set_number_of_spaces_hint (screen, new_num); return; } last_remaining = NULL; extras = NULL; i = 0; for (l = screen->workspaces; l != NULL; 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 ;-) */ need_change_space = FALSE; for (l = extras; l != NULL; l = l->next) { MetaWorkspace *w = l->data; meta_workspace_relocate_windows (w, last_remaining); if (w == screen->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 != NULL; 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 (screen); set_number_of_spaces_hint (screen, new_num); meta_screen_queue_workarea_recalc (screen); for (i = old_num; i < new_num; i++) g_signal_emit (screen, screen_signals[WORKSPACE_ADDED], 0, i); g_object_notify (G_OBJECT (screen), "n-workspaces"); } static int 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); ClutterRect cursor_rect; GList *logical_monitors; GList *l; int 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; ClutterRect logical_monitor_rect = meta_rectangle_to_clutter_rect (&logical_monitor->rect); if (!clutter_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 (MetaCursorSprite *cursor_sprite, int x, int y, MetaScreen *screen) { MetaBackend *backend = meta_get_backend (); if (meta_is_stage_views_scaled ()) { int scale; scale = find_highest_logical_monitor_scale (backend, cursor_sprite); if (scale != 0.0) { meta_cursor_sprite_set_theme_scale (cursor_sprite, scale); meta_cursor_sprite_set_texture_scale (cursor_sprite, 1.0 / 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_set_theme_scale (cursor_sprite, logical_monitor->scale); meta_cursor_sprite_set_texture_scale (cursor_sprite, 1.0); } } } static void manage_root_cursor_sprite_scale (MetaScreen *screen, MetaCursorSprite *cursor_sprite) { g_signal_connect_object (cursor_sprite, "prepare-at", G_CALLBACK (root_cursor_prepare_at), screen, 0); } void meta_screen_update_cursor (MetaScreen *screen) { MetaDisplay *display = screen->display; MetaCursor cursor = screen->current_cursor; Cursor xcursor; MetaCursorSprite *cursor_sprite; MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); cursor_sprite = meta_cursor_sprite_from_theme (cursor); if (meta_is_wayland_compositor ()) manage_root_cursor_sprite_scale (screen, cursor_sprite); meta_cursor_tracker_set_root_cursor (cursor_tracker, cursor_sprite); g_object_unref (cursor_sprite); /* Set a cursor for X11 applications that don't specify their own */ xcursor = meta_display_create_x_cursor (display, cursor); XDefineCursor (display->xdisplay, screen->xroot, xcursor); XFlush (display->xdisplay); XFreeCursor (display->xdisplay, xcursor); } void meta_screen_set_cursor (MetaScreen *screen, MetaCursor cursor) { if (cursor == screen->current_cursor) return; screen->current_cursor = cursor; meta_screen_update_cursor (screen); } static gboolean meta_screen_update_tile_preview_timeout (gpointer data) { MetaScreen *screen = data; MetaWindow *window = screen->display->grab_window; gboolean needs_preview = FALSE; screen->tile_preview_timeout_id = 0; if (window) { switch (screen->preview_tile_mode) { case META_TILE_LEFT: case META_TILE_RIGHT: if (!META_WINDOW_TILED_SIDE_BY_SIDE (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, screen->preview_tile_mode, &tile_rect); meta_compositor_show_tile_preview (screen->display->compositor, window, &tile_rect, monitor); } else meta_compositor_hide_tile_preview (screen->display->compositor); return FALSE; } #define TILE_PREVIEW_TIMEOUT_MS 200 void meta_screen_update_tile_preview (MetaScreen *screen, gboolean delay) { if (delay) { if (screen->tile_preview_timeout_id > 0) return; screen->tile_preview_timeout_id = g_timeout_add (TILE_PREVIEW_TIMEOUT_MS, meta_screen_update_tile_preview_timeout, screen); g_source_set_name_by_id (screen->tile_preview_timeout_id, "[ukwm] meta_screen_update_tile_preview_timeout"); } else { if (screen->tile_preview_timeout_id > 0) g_source_remove (screen->tile_preview_timeout_id); meta_screen_update_tile_preview_timeout ((gpointer)screen); } } void meta_screen_hide_tile_preview (MetaScreen *screen) { if (screen->tile_preview_timeout_id > 0) g_source_remove (screen->tile_preview_timeout_id); screen->tile_preview_timeout_id = 0; screen->preview_tile_mode = META_TILE_NONE; meta_compositor_hide_tile_preview (screen->display->compositor); } MetaWindow* meta_screen_get_mouse_window (MetaScreen *screen, MetaWindow *not_this_one) { 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 (screen->stack, screen->active_workspace, not_this_one, x, y); return window; } int meta_screen_get_monitor_index_for_rect (MetaScreen *screen, 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_screen_get_monitor_neighbor_index (MetaScreen *screen, int which_monitor, MetaScreenDirection 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_screen_get_current_monitor: * @screen: a #MetaScreen * * Gets the index of the monitor that currently has the mouse pointer. * * Return value: a monitor index */ int meta_screen_get_current_monitor (MetaScreen *screen) { 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_screen_get_n_monitors: * @screen: a #MetaScreen * * Gets the number of monitors that are joined together to form @screen. * * Return value: the number of monitors */ int meta_screen_get_n_monitors (MetaScreen *screen) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); g_return_val_if_fail (META_IS_SCREEN (screen), 0); return meta_monitor_manager_get_num_logical_monitors (monitor_manager); } /** * meta_screen_get_primary_monitor: * @screen: a #MetaScreen * * Gets the index of the primary monitor on this @screen. * * Return value: a monitor index */ int meta_screen_get_primary_monitor (MetaScreen *screen) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaLogicalMonitor *logical_monitor; g_return_val_if_fail (META_IS_SCREEN (screen), 0); logical_monitor = meta_monitor_manager_get_primary_logical_monitor (monitor_manager); if (logical_monitor) return logical_monitor->number; else return -1; } /** * meta_screen_get_monitor_geometry: * @screen: a #MetaScreen * @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_screen_get_monitor_geometry (MetaScreen *screen, 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_SCREEN (screen)); 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; } #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_screen_update_workspace_layout (MetaScreen *screen) { uint32_t *list; int n_items; if (screen->workspace_layout_overridden) return; list = NULL; n_items = 0; if (meta_prop_get_cardinal_list (screen->display, screen->xroot, screen->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: screen->vertical_workspaces = FALSE; break; case _NET_WM_ORIENTATION_VERT: screen->vertical_workspaces = 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) screen->rows_of_workspaces = rows; else screen->rows_of_workspaces = -1; if (cols > 0) screen->columns_of_workspaces = cols; else screen->columns_of_workspaces = -1; } if (n_items == 4) { switch (list[3]) { case _NET_WM_TOPLEFT: screen->starting_corner = META_SCREEN_TOPLEFT; break; case _NET_WM_TOPRIGHT: screen->starting_corner = META_SCREEN_TOPRIGHT; break; case _NET_WM_BOTTOMRIGHT: screen->starting_corner = META_SCREEN_BOTTOMRIGHT; break; case _NET_WM_BOTTOMLEFT: screen->starting_corner = META_SCREEN_BOTTOMLEFT; break; default: meta_warning ("Someone set a weird starting corner in _NET_DESKTOP_LAYOUT\n"); break; } } else screen->starting_corner = META_SCREEN_TOPLEFT; } else { meta_warning ("Someone set _NET_DESKTOP_LAYOUT to %d integers instead of 4 " "(3 is accepted for backwards compat)\n", n_items); } meta_XFree (list); } meta_verbose ("Workspace layout rows = %d cols = %d orientation = %d starting corner = %u\n", screen->rows_of_workspaces, screen->columns_of_workspaces, screen->vertical_workspaces, screen->starting_corner); } /** * meta_screen_override_workspace_layout: * @screen: a #MetaScreen * @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_screen_override_workspace_layout (MetaScreen *screen, MetaScreenCorner starting_corner, gboolean vertical_layout, int n_rows, int n_columns) { g_return_if_fail (META_IS_SCREEN (screen)); g_return_if_fail (n_rows > 0 || n_columns > 0); g_return_if_fail (n_rows != 0 && n_columns != 0); screen->workspace_layout_overridden = TRUE; screen->vertical_workspaces = vertical_layout != FALSE; screen->starting_corner = starting_corner; screen->rows_of_workspaces = n_rows; screen->columns_of_workspaces = n_columns; /* In theory we should remove _NET_DESKTOP_LAYOUT from _NET_SUPPORTED at this * point, but it's unlikely that anybody checks that, and it's unlikely that * anybody who checks that handles changes, so we'd probably just create * a race condition. And it's hard to implement with the code in set_supported_hint() */ } static void set_workspace_names (MetaScreen *screen) { /* This updates names on root window when the pref changes, * note we only get prefs change notify if things have * really changed. */ GString *flattened; int i; int n_spaces; /* flatten to nul-separated list */ n_spaces = meta_screen_get_n_workspaces (screen); 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_error_trap_push (screen->display); XChangeProperty (screen->display->xdisplay, screen->xroot, screen->display->atom__NET_DESKTOP_NAMES, screen->display->atom_UTF8_STRING, 8, PropModeReplace, (unsigned char *)flattened->str, flattened->len); meta_error_trap_pop (screen->display); g_string_free (flattened, TRUE); } void meta_screen_update_workspace_names (MetaScreen *screen) { 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 (screen->display, screen->xroot, screen->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); } Window meta_create_offscreen_window (Display *xdisplay, 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 (xdisplay, parent, -100, -100, 1, 1, 0, CopyFromParent, CopyFromParent, (Visual *)CopyFromParent, CWOverrideRedirect | CWEventMask, &attrs); } static void set_work_area_hint (MetaScreen *screen) { int num_workspaces; GList *l; unsigned long *data, *tmp; MetaRectangle area; num_workspaces = meta_screen_get_n_workspaces (screen); data = g_new (unsigned long, num_workspaces * 4); tmp = data; for (l = screen->workspaces; l != NULL; l = l->next) { MetaWorkspace *workspace = l->data; meta_workspace_get_work_area_all_monitors (workspace, &area); tmp[0] = area.x; tmp[1] = area.y; tmp[2] = area.width; tmp[3] = area.height; tmp += 4; } meta_error_trap_push (screen->display); XChangeProperty (screen->display->xdisplay, screen->xroot, screen->display->atom__NET_WORKAREA, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, num_workspaces*4); g_free (data); meta_error_trap_pop (screen->display); g_signal_emit (screen, screen_signals[WORKAREAS_CHANGED], 0); } static gboolean set_work_area_later_func (MetaScreen *screen) { meta_topic (META_DEBUG_WORKAREA, "Running work area hint computation function\n"); screen->work_area_later = 0; set_work_area_hint (screen); return FALSE; } void meta_screen_queue_workarea_recalc (MetaScreen *screen) { /* Recompute work area later before redrawing */ if (screen->work_area_later == 0) { meta_topic (META_DEBUG_WORKAREA, "Adding work area hint computation function\n"); screen->work_area_later = meta_later_add (META_LATER_BEFORE_REDRAW, (GSourceFunc) set_work_area_later_func, screen, NULL); } } #ifdef WITH_VERBOSE_MODE static const char * meta_screen_corner_to_string (MetaScreenCorner corner) { switch (corner) { case META_SCREEN_TOPLEFT: return "TopLeft"; case META_SCREEN_TOPRIGHT: return "TopRight"; case META_SCREEN_BOTTOMLEFT: return "BottomLeft"; case META_SCREEN_BOTTOMRIGHT: return "BottomRight"; } return "Unknown"; } #endif /* WITH_VERBOSE_MODE */ void meta_screen_calc_workspace_layout (MetaScreen *screen, 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 = screen->rows_of_workspaces; cols = screen->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, screen->vertical_workspaces ? "(true)" : "(false)", meta_screen_corner_to_string (screen->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_SCREEN_TOPLEFT * vertical_workspaces = 0 vertical_workspaces=1 * 1234 1357 * 5678 2468 * * starting_corner = META_SCREEN_TOPRIGHT * vertical_workspaces = 0 vertical_workspaces=1 * 4321 7531 * 8765 8642 * * starting_corner = META_SCREEN_BOTTOMLEFT * vertical_workspaces = 0 vertical_workspaces=1 * 5678 2468 * 1234 1357 * * starting_corner = META_SCREEN_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); current_row = -1; current_col = -1; i = 0; switch (screen->starting_corner) { case META_SCREEN_TOPLEFT: if (screen->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_SCREEN_TOPRIGHT: if (screen->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_SCREEN_BOTTOMLEFT: if (screen->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_SCREEN_BOTTOMRIGHT: if (screen->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_screen_free_workspace_layout (MetaWorkspaceLayout *layout) { g_free (layout->grid); } static void meta_screen_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 *manager, MetaScreen *screen) { meta_monitor_manager_get_screen_size (manager, &screen->rect.width, &screen->rect.height); reload_logical_monitors (screen); set_desktop_geometry_hint (screen); /* Resize the guard window to fill the screen again. */ if (screen->guard_window != None) { XWindowChanges changes; changes.x = 0; changes.y = 0; changes.width = screen->rect.width; changes.height = screen->rect.height; XConfigureWindow(screen->display->xdisplay, screen->guard_window, CWX | CWY | CWWidth | CWHeight, &changes); } /* Fix up monitor for all windows on this screen */ meta_screen_foreach_window (screen, META_LIST_INCLUDE_OVERRIDE_REDIRECT, (MetaScreenWindowFunc) meta_window_update_for_monitors_changed, 0); /* Queue a resize on all the windows */ meta_screen_foreach_window (screen, META_LIST_DEFAULT, meta_screen_resize_func, 0); meta_screen_queue_check_fullscreen (screen); } static void on_monitors_changed (MetaMonitorManager *manager, MetaScreen *screen) { /* Inform the external world about what has happened */ g_signal_emit (screen, screen_signals[MONITORS_CHANGED], 0); } void meta_screen_update_showing_desktop_hint (MetaScreen *screen) { unsigned long data[1]; data[0] = screen->active_workspace->showing_desktop ? 1 : 0; meta_error_trap_push (screen->display); XChangeProperty (screen->display->xdisplay, screen->xroot, screen->display->atom__NET_SHOWING_DESKTOP, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 1); meta_error_trap_pop (screen->display); } static void queue_windows_showing (MetaScreen *screen) { 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 (screen->display, META_LIST_DEFAULT); for (l = windows; l != NULL; l = l->next) { MetaWindow *w = l->data; meta_window_queue (w, META_QUEUE_CALC_SHOWING); } g_slist_free (windows); } void meta_screen_minimize_all_on_active_workspace_except (MetaScreen *screen, MetaWindow *keep) { GList *l; for (l = screen->active_workspace->windows; l != NULL; l = l->next) { MetaWindow *w = l->data; if (w->has_minimize_func && w != keep) meta_window_minimize (w); } } void meta_screen_show_desktop (MetaScreen *screen, guint32 timestamp) { GList *l; if (screen->active_workspace->showing_desktop) return; screen->active_workspace->showing_desktop = TRUE; queue_windows_showing (screen); /* Focus the most recently used META_WINDOW_DESKTOP window, if there is one; * see bug 159257. */ for (l = screen->active_workspace->mru_list; l != NULL; l = l->next) { MetaWindow *w = l->data; if (w->type == META_WINDOW_DESKTOP) { meta_window_focus (w, timestamp); break; } } meta_screen_update_showing_desktop_hint (screen); } void meta_screen_unshow_desktop (MetaScreen *screen) { if (!screen->active_workspace->showing_desktop) return; screen->active_workspace->showing_desktop = FALSE; queue_windows_showing (screen); meta_screen_update_showing_desktop_hint (screen); } /** * meta_screen_get_startup_sequences: (skip) * @screen: * * Return value: (transfer none): Currently active #SnStartupSequence items */ GSList * meta_screen_get_startup_sequences (MetaScreen *screen) { return screen->startup_sequences; } /* 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_screen_apply_startup_properties (MetaScreen *screen, MetaWindow *window) { #ifdef HAVE_STARTUP_NOTIFICATION const char *startup_id; GSList *l; SnStartupSequence *sequence; /* 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)"); sequence = NULL; if (startup_id == NULL) { /* 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. */ for (l = screen->startup_sequences; l != NULL; l = l->next) { const char *wmclass; SnStartupSequence *seq = l->data; wmclass = sn_startup_sequence_get_wmclass (seq); if (wmclass != NULL && ((window->res_class && strcmp (wmclass, window->res_class) == 0) || (window->res_name && strcmp (wmclass, window->res_name) == 0))) { sequence = seq; g_assert (window->startup_id == NULL); window->startup_id = g_strdup (sn_startup_sequence_get_id (sequence)); startup_id = window->startup_id; meta_topic (META_DEBUG_STARTUP, "Ending legacy sequence %s due to window %s\n", sn_startup_sequence_get_id (sequence), window->desc); sn_startup_sequence_complete (sequence); break; } } } /* Still no startup ID? Bail. */ if (startup_id == NULL) 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) { for (l = screen->startup_sequences; l != NULL; l = l->next) { SnStartupSequence *seq = l->data; const char *id; id = sn_startup_sequence_get_id (seq); if (strcmp (id, startup_id) == 0) { sequence = seq; break; } } } 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 = sn_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 = sn_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); } #endif /* HAVE_STARTUP_NOTIFICATION */ return FALSE; } int meta_screen_get_screen_number (MetaScreen *screen) { return meta_ui_get_screen_number (); } /** * meta_screen_get_display: * @screen: A #MetaScreen * * Retrieve the display associated with screen. * * Returns: (transfer none): Display */ MetaDisplay * meta_screen_get_display (MetaScreen *screen) { return screen->display; } /** * meta_screen_get_xroot: (skip) * @screen: A #MetaScreen * */ Window meta_screen_get_xroot (MetaScreen *screen) { return screen->xroot; } /** * meta_screen_get_size: * @screen: A #MetaScreen * @width: (out): The width of the screen * @height: (out): The height of the screen * * Retrieve the size of the screen. */ void meta_screen_get_size (MetaScreen *screen, int *width, int *height) { if (width != NULL) *width = screen->rect.width; if (height != NULL) *height = screen->rect.height; } void meta_screen_set_cm_selection (MetaScreen *screen) { char selection[32]; Atom a; guint32 timestamp; timestamp = meta_display_get_current_time_roundtrip (screen->display); g_snprintf (selection, sizeof (selection), "_NET_WM_CM_S%d", meta_ui_get_screen_number ()); a = XInternAtom (screen->display->xdisplay, selection, False); screen->wm_cm_selection_window = take_manager_selection (screen->display, screen->xroot, a, timestamp, TRUE); } /** * meta_screen_get_workspaces: (skip) * @screen: a #MetaScreen * * Returns: (transfer none) (element-type Meta.Workspace): The workspaces for @screen */ GList * meta_screen_get_workspaces (MetaScreen *screen) { return screen->workspaces; } int meta_screen_get_active_workspace_index (MetaScreen *screen) { MetaWorkspace *active = screen->active_workspace; if (!active) return -1; return meta_workspace_index (active); } /** * meta_screen_get_active_workspace: * @screen: A #MetaScreen * * Returns: (transfer none): The current workspace */ MetaWorkspace * meta_screen_get_active_workspace (MetaScreen *screen) { return screen->active_workspace; } void meta_screen_focus_default_window (MetaScreen *screen, guint32 timestamp) { meta_workspace_focus_default_window (screen->active_workspace, NULL, timestamp); } void meta_screen_restacked (MetaScreen *screen) { g_signal_emit (screen, screen_signals[RESTACKED], 0); } void meta_screen_workspace_switched (MetaScreen *screen, int from, int to, MetaMotionDirection direction) { g_signal_emit (screen, screen_signals[WORKSPACE_SWITCHED], 0, from, to, direction); } void meta_screen_set_active_workspace_hint (MetaScreen *screen) { 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 (screen->closing > 0) return; data[0] = meta_workspace_index (screen->active_workspace); meta_verbose ("Setting _NET_CURRENT_DESKTOP to %lu\n", data[0]); meta_error_trap_push (screen->display); XChangeProperty (screen->display->xdisplay, screen->xroot, screen->display->atom__NET_CURRENT_DESKTOP, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 1); meta_error_trap_pop (screen->display); } static gboolean check_fullscreen_func (gpointer data) { MetaScreen *screen = 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; screen->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 (screen->stack); window; window = meta_stack_get_below (screen->stack, window, FALSE)) { gboolean covers_monitors = FALSE; if (window->screen != screen || 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 (screen->stack); if (window) meta_stack_update_layer (screen->stack, window); g_signal_emit (screen, screen_signals[IN_FULLSCREEN_CHANGED], 0, NULL); } return FALSE; } void meta_screen_queue_check_fullscreen (MetaScreen *screen) { if (!screen->check_fullscreen_later) screen->check_fullscreen_later = meta_later_add (META_LATER_CHECK_FULLSCREEN, check_fullscreen_func, screen, NULL); } /** * meta_screen_get_monitor_in_fullscreen: * @screen: a #MetaScreen * @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 * MetaScreen::in-fullscreen-changed. * * Returns: %TRUE if there is a fullscreen window covering the specified monitor. */ gboolean meta_screen_get_monitor_in_fullscreen (MetaScreen *screen, 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_SCREEN (screen), 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; } gboolean meta_screen_handle_xevent (MetaScreen *screen, XEvent *xevent) { MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); if (meta_cursor_tracker_handle_xevent (cursor_tracker, xevent)) return TRUE; return FALSE; } ukwm/src/core/meta-gesture-tracker-private.h0000664000175000017500000000624713233511035020063 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author: Carlos Garnacho */ #ifndef META_GESTURE_TRACKER_PRIVATE_H #define META_GESTURE_TRACKER_PRIVATE_H #include #include #include #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; typedef enum { META_SEQUENCE_NONE, META_SEQUENCE_ACCEPTED, META_SEQUENCE_REJECTED, META_SEQUENCE_PENDING_END } MetaSequenceState; 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 */ ukwm/src/core/startup-notification-private.h0000664000175000017500000000365313220600404020207 0ustar fengfeng/* -*- 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 "display-private.h" #define META_TYPE_STARTUP_NOTIFICATION (meta_startup_notification_get_type ()) G_DECLARE_FINAL_TYPE (MetaStartupNotification, meta_startup_notification, META, STARTUP_NOTIFICATION, GObject) MetaStartupNotification * meta_startup_notification_get (MetaDisplay *display); gboolean meta_startup_notification_handle_xevent (MetaStartupNotification *sn, XEvent *xevent); void meta_startup_notification_remove_sequence (MetaStartupNotification *sn, const gchar *id); GSList * meta_startup_notification_get_sequences (MetaStartupNotification *sn); #endif /* META_STARTUP_NOTIFICATION_PRIVATE_H */ ukwm/src/core/constraints.h0000664000175000017500000000250613220600404014714 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm size/position constraints */ /* * Copyright (C) 2002 Red Hat, Inc. * Copyright (C) 2005 Elijah Newren * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_CONSTRAINTS_H #define META_CONSTRAINTS_H #include #include "window-private.h" #include "frame.h" void meta_window_constrain (MetaWindow *window, MetaMoveResizeFlags flags, int resize_gravity, const MetaRectangle *orig, MetaRectangle *new); #endif /* META_CONSTRAINTS_H */ ukwm/src/core/util-private.h0000664000175000017500000000263613233511035015003 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm utilities */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2005 Elijah Newren * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef META_UTIL_PRIVATE_H #define META_UTIL_PRIVATE_H #include #include void meta_set_verbose (gboolean setting); 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 ukwm/src/core/edge-resistance.c0000664000175000017500000013245713220600404015413 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Edge resistance for move/resize operations */ /* * Copyright (C) 2005, 2006 Elijah Newren * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include #include "edge-resistance.h" #include "boxes-private.h" #include "display-private.h" #include "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 (); } } 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; if (resistance_data->timeout_id != 0) { g_source_remove (resistance_data->timeout_id); resistance_data->timeout_id = 0; } } /* 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, "[ukwm] 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_SIDE_BY_SIDE (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_SIDE_BY_SIDE (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]); 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; } } } 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 && edge_data->left_data.timeout_id != 0) g_source_remove (edge_data->left_data.timeout_id); if (edge_data->right_data.timeout_setup && edge_data->right_data.timeout_id != 0) g_source_remove (edge_data->right_data.timeout_id); if (edge_data->top_data.timeout_setup && edge_data->top_data.timeout_id != 0) g_source_remove (edge_data->top_data.timeout_id); if (edge_data->bottom_data.timeout_setup && edge_data->bottom_data.timeout_id != 0) g_source_remove (edge_data->bottom_data.timeout_id); 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; 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->screen->stack, display->screen->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 reduced; /* 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->screen->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); /* FIXME: Shouldn't there be a helper function to make this one line of code * to free a list instead of four ugly ones? */ g_slist_foreach (obscuring_windows, (void (*)(gpointer,gpointer))&g_free, /* ew, for ugly */ NULL); g_slist_free (obscuring_windows); /* 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, display->screen->active_workspace->monitor_edges, display->screen->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, int 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); } } ukwm/src/core/prefs.c0000664000175000017500000016501613260055411013473 0ustar fengfeng/* -*- 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. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ /** * SECTION:prefs * @title: Preferences * @short_description: Ukwm preferences */ #include #include #include "util-private.h" #include "meta-plugin-manager.h" #include #include #include #include #include "keybindings-private.h" #include "meta-accel-parse.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" /* For UKUI theme control */ #define KEY_UKUI_GTK_THEME "gtk-theme" #define KEY_XKB_OPTIONS "xkb-options" #define KEY_OVERLAY_KEY "overlay-key" #define KEY_WORKSPACES_ONLY_ON_PRIMARY "workspaces-only-on-primary" /* These are the different schemas we are keeping * a GSettings instance for */ #define SCHEMA_GENERAL "org.gnome.desktop.wm.preferences" #define SCHEMA_UKWM "org.ukui.ukwm" #define SCHEMA_INTERFACE "org.gnome.desktop.interface" /* For UKUI */ #define SCHEMA_MATE_INTERFACE "org.mate.interface" #define SCHEMA_INPUT_SOURCES "org.gnome.desktop.input-sources" #define SCHEMA_XSETTINGS "org.ukui.SettingsDaemon.plugins.xsettings" #define SCHEMA_MOUSE "org.mate.peripherals-mouse" #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 MetaKeyCombo overlay_key_combo = { 0, 0, 0 }; static GDesktopFocusMode focus_mode = G_DESKTOP_FOCUS_MODE_CLICK; static GDesktopFocusNewWindows focus_new_windows = G_DESKTOP_FOCUS_NEW_WINDOWS_SMART; static gboolean raise_on_click = TRUE; static gboolean center_new_windows = FALSE; static gboolean attach_modal_dialogs = FALSE; /* For UKUI theme control */ static char* current_theme = NULL; static int num_workspaces = 4; static GDesktopTitlebarAction action_double_click_titlebar = G_DESKTOP_TITLEBAR_ACTION_TOGGLE_MAXIMIZE; static GDesktopTitlebarAction action_middle_click_titlebar = G_DESKTOP_TITLEBAR_ACTION_LOWER; static GDesktopTitlebarAction action_right_click_titlebar = G_DESKTOP_TITLEBAR_ACTION_MENU; 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 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 ignore_request_hide_titlebar = FALSE; static gboolean auto_maximize = TRUE; static gboolean show_fallback_app_menu = FALSE; static GDesktopVisualBellType visual_bell_type = G_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 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 wayland_settings_changed (GSettings *settings, gchar *key, gpointer data); static void settings_changed (GSettings *settings, gchar *key, gpointer data); static void bindings_changed (GSettings *settings, gchar *key, gpointer data); static void shell_shows_app_menu_changed (GtkSettings *settings, GParamSpec *pspec, gpointer data); static void update_cursor_size_from_gtk (GtkSettings *settings, GParamSpec *pspec, gpointer data); static void update_cursor_size (void); 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 button_layout_handler (GVariant*, gpointer*, gpointer); static gboolean overlay_key_handler (GVariant*, gpointer*, gpointer); static gboolean iso_next_group_handler (GVariant*, gpointer*, gpointer); static void do_override (char *key, char *schema); 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; /* 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, }, { { "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, }, { { NULL, 0, 0 }, NULL }, }; static MetaBoolPreference preferences_bool[] = { { { "attach-modal-dialogs", SCHEMA_UKWM, META_PREF_ATTACH_MODAL_DIALOGS, }, &attach_modal_dialogs, }, { { "center-new-windows", SCHEMA_UKWM, META_PREF_CENTER_NEW_WINDOWS, }, ¢er_new_windows, }, { { "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_UKWM, 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_UKWM, 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_UKWM, META_PREF_EDGE_TILING, }, &edge_tiling, }, { { "workspaces-only-on-primary", SCHEMA_UKWM, META_PREF_WORKSPACES_ONLY_ON_PRIMARY, }, &workspaces_only_on_primary, }, { { "auto-maximize", SCHEMA_UKWM, META_PREF_AUTO_MAXIMIZE, }, &auto_maximize, }, { { NULL, 0, 0 }, NULL }, }; static MetaStringPreference preferences_string[] = { { { "mouse-button-modifier", SCHEMA_GENERAL, META_PREF_MOUSE_BUTTON_MODS, }, mouse_button_mods_handler, NULL, }, { { KEY_UKUI_GTK_THEME, SCHEMA_MATE_INTERFACE, META_PREF_THEME, }, NULL, ¤t_theme, }, { { 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_UKWM, META_PREF_KEYBINDINGS, }, overlay_key_handler, NULL, }, { { 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_UKWM, META_PREF_DRAGGABLE_BORDER_WIDTH, }, &draggable_border_width }, { { "drag-threshold", SCHEMA_MOUSE, META_PREF_DRAG_THRESHOLD, }, &drag_threshold }, { { NULL, 0, 0 }, NULL }, }; /* * This is used to keep track of override schemas used to * override preferences from the "normal" metacity/ukwm * schemas; we modify the preferences arrays directly, but * we also need to remember what we have done to handle * subsequent overrides correctly. */ typedef struct { char *key; char *new_schema; } MetaPrefsOverriddenKey; static GSList *overridden_keys; 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_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); } } /****************************************************************************/ /* 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; } meta_bug ("Did not find listener to remove\n"); } 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, "[ukwm] changed_idle_handler"); } } /****************************************************************************/ /* Initialisation. */ /****************************************************************************/ void meta_prefs_init (void) { GSettings *settings; GSList *tmp; 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_UKWM); g_signal_connect (settings, "changed", G_CALLBACK (settings_changed), NULL); g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_UKWM), 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); /* Individual keys we watch outside of our schemas */ /* For UKUI theme control */ settings = g_settings_new (SCHEMA_MATE_INTERFACE); g_signal_connect (settings, "changed::" KEY_UKUI_GTK_THEME, G_CALLBACK (settings_changed), NULL); g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_MATE_INTERFACE), settings); 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); if (meta_is_wayland_compositor ()) g_signal_connect (settings, "changed::cursor-size", G_CALLBACK (wayland_settings_changed), NULL); g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_INTERFACE), settings); g_signal_connect (gtk_settings_get_default (), "notify::gtk-shell-shows-app-menu", G_CALLBACK (shell_shows_app_menu_changed), NULL); if (!meta_is_wayland_compositor ()) g_signal_connect (gtk_settings_get_default (), "notify::gtk-cursor-theme-size", G_CALLBACK (update_cursor_size_from_gtk), NULL); 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); for (tmp = overridden_keys; tmp; tmp = tmp->next) { MetaPrefsOverriddenKey *override = tmp->data; do_override (override->key, override->new_schema); } /* 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 (); update_cursor_size (); shell_shows_app_menu_changed (gtk_settings_get_default (), NULL, NULL); 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; } static void do_override (char *key, char *schema) { MetaBasePreference *pref; GSettings *settings; char *detailed_signal; gpointer data; guint handler_id; g_return_if_fail (settings_schemas != NULL); if (!find_pref (preferences_enum, sizeof(MetaEnumPreference), key, &pref) && !find_pref (preferences_bool, sizeof(MetaBoolPreference), key, &pref) && !find_pref (preferences_string, sizeof(MetaStringPreference), key, &pref) && !find_pref (preferences_int, sizeof(MetaIntPreference), key, &pref)) { meta_warning ("Can't override preference key, \"%s\" not found\n", key); return; } settings = SETTINGS (pref->schema); data = g_object_get_data (G_OBJECT (settings), key); if (data) { handler_id = GPOINTER_TO_UINT (data); g_signal_handler_disconnect (settings, handler_id); } pref->schema = schema; settings = SETTINGS (pref->schema); if (!settings) { settings = g_settings_new (pref->schema); g_hash_table_insert (settings_schemas, g_strdup (pref->schema), settings); } detailed_signal = g_strdup_printf ("changed::%s", key); handler_id = g_signal_connect (settings, detailed_signal, G_CALLBACK (settings_changed), NULL); g_free (detailed_signal); g_object_set_data (G_OBJECT (settings), key, GUINT_TO_POINTER (handler_id)); settings_changed (settings, key, NULL); } /** * meta_prefs_override_preference_schema: * @key: the preference name * @schema: new schema for preference @key * * Specify a schema whose keys are used to override the standard Metacity * keys. This might be used if a plugin expected a different value for * some preference than the Metacity default. While this function can be * called at any point, this function should generally be called in a * plugin's constructor, rather than in its start() method so the preference * isn't first loaded with one value then changed to another value. */ void meta_prefs_override_preference_schema (const char *key, const char *schema) { MetaPrefsOverriddenKey *overridden; GSList *tmp; /* Merge identical overrides, this isn't an error */ for (tmp = overridden_keys; tmp; tmp = tmp->next) { MetaPrefsOverriddenKey *tmp_overridden = tmp->data; if (strcmp (tmp_overridden->key, key) == 0 && strcmp (tmp_overridden->new_schema, schema) == 0) return; } overridden = NULL; for (tmp = overridden_keys; tmp; tmp = tmp->next) { MetaPrefsOverriddenKey *tmp_overridden = tmp->data; if (strcmp (tmp_overridden->key, key) == 0) overridden = tmp_overridden; } if (overridden) { g_free (overridden->new_schema); overridden->new_schema = g_strdup (schema); } else { overridden = g_slice_new (MetaPrefsOverriddenKey); overridden->key = g_strdup (key); overridden->new_schema = g_strdup (schema); overridden_keys = g_slist_prepend (overridden_keys, overridden); } if (settings_schemas != NULL) do_override (overridden->key, overridden->new_schema); } /****************************************************************************/ /* Updates. */ /****************************************************************************/ static void wayland_settings_changed (GSettings *settings, gchar *key, gpointer data) { GVariant *value = g_settings_get_value (settings, key); const GVariantType *type = g_variant_get_type (value); g_return_if_fail (g_variant_type_equal (type, G_VARIANT_TYPE_INT32)); g_return_if_fail (g_str_equal (key, "cursor-size")); update_cursor_size (); } 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)) handle_preference_update_int (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); } static void shell_shows_app_menu_changed (GtkSettings *settings, GParamSpec *pspec, gpointer data) { int shell_shows_app_menu = 1; gboolean changed = FALSE; g_object_get (settings, "gtk-shell-shows-app-menu", &shell_shows_app_menu, NULL); changed = (show_fallback_app_menu == !!shell_shows_app_menu); show_fallback_app_menu = !shell_shows_app_menu; if (changed) queue_changed (META_PREF_BUTTON_LAYOUT); } static void update_cursor_size (void) { if (meta_is_wayland_compositor ()) { /* When running as a Wayland compositor, since we size of the cursor * depends on what output it is on, we cannot use the GTK+ * "gtk-cursor-theme-size" setting because it has already been multiplied * by the primary monitor scale. So, instead get the non-premultiplied * cursor size value directly from gsettings instead. */ cursor_size = g_settings_get_int (SETTINGS (SCHEMA_INTERFACE), "cursor-size"); } else { update_cursor_size_from_gtk (gtk_settings_get_default (), NULL, NULL); } } static void update_cursor_size_from_gtk (GtkSettings *settings, GParamSpec *pspec, gpointer data) { GdkScreen *screen = gdk_screen_get_default (); GValue value = G_VALUE_INIT; int xsettings_cursor_size = 24; g_value_init (&value, G_TYPE_INT); if (gdk_screen_get_setting (screen, "gtk-cursor-theme-size", &value)) { xsettings_cursor_size = g_value_get_int (&value); } if (xsettings_cursor_size != cursor_size) { cursor_size = xsettings_cursor_size; queue_changed (META_PREF_CURSOR_SIZE); } } /** * 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; } GDesktopFocusMode meta_prefs_get_focus_mode (void) { return focus_mode; } GDesktopFocusNewWindows meta_prefs_get_focus_new_windows (void) { return focus_new_windows; } gboolean meta_prefs_get_center_new_windows (void) { return center_new_windows; } 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_show_fallback_app_menu (void) { return show_fallback_app_menu; } /* For UKUI theme control */ const char* meta_prefs_get_theme (void) { return current_theme; } 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 mouse_button_mods_handler (GVariant *value, gpointer *result, gpointer data) { 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 != mouse_button_mods) { mouse_button_mods = mods; queue_changed (META_PREF_MOUSE_BUTTON_MODS); } return TRUE; } 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, "appmenu") == 0) return META_BUTTON_FUNCTION_APPMENU; 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 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; } #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_FOCUS_MODE: return "FOCUS_MODE"; case META_PREF_FOCUS_NEW_WINDOWS: return "FOCUS_NEW_WINDOWS"; case META_PREF_CENTER_NEW_WINDOWS: return "CENTER_NEW_WINDOWS"; case META_PREF_ATTACH_MODAL_DIALOGS: return "ATTACH_MODAL_DIALOGS"; case META_PREF_RAISE_ON_CLICK: return "RAISE_ON_CLICK"; /* For UKUI theme control */ case META_PREF_THEME: return "THEME"; 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_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"; } 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_object_unref (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); pref = g_new0 (MetaKeyPref, 1); pref->name = g_strdup ("overlay-key"); pref->action = META_KEYBINDING_ACTION_OVERLAY_KEY; pref->combos = g_slist_prepend (pref->combos, &overlay_key_combo); pref->builtin = 1; g_hash_table_insert (key_bindings, g_strdup ("overlay-key"), pref); } 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; } 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; } GDesktopVisualBellType meta_prefs_get_visual_bell_type (void) { return visual_bell_type; } gboolean meta_prefs_add_keybinding (const char *name, GSettings *settings, MetaKeyBindingAction action, MetaKeyBindingFlags flags) { MetaKeyPref *pref; char **strokes; 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 = g_object_ref (settings); 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)); } } else { 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)); queue_changed (META_PREF_KEYBINDINGS); } strokes = g_settings_get_strv (settings, name); 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; guint 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; } id = GPOINTER_TO_UINT (g_object_steal_data (G_OBJECT (pref->settings), name)); g_signal_handler_disconnect (pref->settings, id); 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; } const char * meta_prefs_get_iso_next_group_option (void) { return iso_next_group_option; } GDesktopTitlebarAction meta_prefs_get_action_double_click_titlebar (void) { return action_double_click_titlebar; } GDesktopTitlebarAction meta_prefs_get_action_middle_click_titlebar (void) { return action_middle_click_titlebar; } GDesktopTitlebarAction meta_prefs_get_action_right_click_titlebar (void) { return action_right_click_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_auto_maximize (void) { return auto_maximize; } 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; } gboolean meta_prefs_get_ignore_request_hide_titlebar (void) { return ignore_request_hide_titlebar; } void meta_prefs_set_ignore_request_hide_titlebar (gboolean whether) { ignore_request_hide_titlebar = whether; } ukwm/src/core/restart-helper.c0000664000175000017500000000463713220600404015310 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * SECTION:restart-helper * @short_description: helper program during a restart * * To smoothly restart Ukwm, 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 Ukwm picks it back up. */ /* * Copyright (C) 2014 Red Hat, Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #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, "_UKWM_RESTART_HELPER", False), selection_window, CurrentTime); /* Ukwm 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 Ukwm starts restarting. */ XSync (display, False); printf ("STARTED\n"); fflush (stdout); while (True) { XEvent xev; XNextEvent (display, &xev); /* Ukwm restarted and unset the selection to indicate that * it has a reference on the COW again */ if (xev.xany.type == SelectionClear) return 0; } } ukwm/src/core/meta-fraction.c0000664000175000017500000000601613233510055015077 0ustar fengfeng/* * 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 }; } ukwm/src/core/meta-border.c0000664000175000017500000001034313233511035014544 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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); } ukwm/src/wayland/0000775000175000017500000000000013260055411012706 5ustar fengfengukwm/src/wayland/meta-wayland-pointer-constraints.c0000664000175000017500000011307713233511035021470 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #include "config.h" #include "meta-wayland-pointer-constraints.h" #include #include "meta/meta-backend.h" #include "meta-wayland-private.h" #include "meta-wayland-seat.h" #include "meta-wayland-pointer.h" #include "meta-wayland-surface.h" #include "meta-wayland-region.h" #include "meta-xwayland.h" #include "meta-pointer-lock-wayland.h" #include "meta-pointer-confinement-wayland.h" #include "window-private.h" #include "backends/meta-backend-private.h" #include "backends/native/meta-backend-native.h" #include "backends/meta-pointer-constraint.h" #include "core/frame.h" #include "pointer-constraints-unstable-v1-server-protocol.h" 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; connect_window (data, surface->window); g_signal_handler_disconnect (surface, data->window_associated_handler_id); data->window_associated_handler_id = 0; meta_wayland_pointer_constraint_maybe_enable_for_window (surface->window); } static MetaWaylandSurfacePointerConstraintsData * surface_constraint_data_new (MetaWaylandSurface *surface) { MetaWaylandSurfacePointerConstraintsData *data; data = g_new0 (MetaWaylandSurfacePointerConstraintsData, 1); data->surface = surface; if (surface->window) { connect_window (data, surface->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_signal_handler_disconnect (data->window, data->appears_changed_handler_id); g_signal_handler_disconnect (data->window, data->raised_handler_id); g_object_remove_weak_pointer (G_OBJECT (data->window), (gpointer *) &data->window); } else { g_signal_handler_disconnect (data->surface->role, data->window_associated_handler_id); } 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 = constraint->surface->window; 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_wayland_pointer_end_grab (constraint->grab.pointer); meta_backend_set_client_pointer_constraint (meta_get_backend (), NULL); } 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 = constraint->surface->window; 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 { MetaWindow *window = constraint->surface->window; 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 = constraint->surface->window; 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); 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 (MetaWaylandPendingState *pending) { return g_object_get_qdata (G_OBJECT (pending), quark_pending_constraint_state); } static MetaWaylandPendingConstraintState * get_pending_constraint_state (MetaWaylandPointerConstraint *constraint) { MetaWaylandPendingState *pending = constraint->surface->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) 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 (MetaWaylandPendingState *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, MetaWaylandPendingState *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 (MetaWaylandPendingState *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_signal_handler_disconnect (pending, constraint_pending->applied_handler_id); 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) { MetaWaylandPendingState *pending = constraint->surface->pending; MetaWaylandPendingConstraintStateContainer *container; MetaWaylandPendingConstraintState *constraint_pending; 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 as 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; } 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) meta_backend_warp_pointer (meta_get_backend (), 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_signal_handler_disconnect (constraint->seat->pointer, constraint->pointer_focus_surface_handler_id); 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"); } ukwm/src/wayland/meta-wayland-popup.h0000664000175000017500000000434313220600404016601 0ustar fengfeng/* * 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 "meta-wayland-types.h" #include "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 */ ukwm/src/wayland/meta-wayland-tablet-tool.c0000664000175000017500000007476313233511035017701 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author: Carlos Garnacho */ #define _GNU_SOURCE #include "config.h" #include #include #include #include "tablet-unstable-v2-server-protocol.h" #include "meta-wayland-private.h" #include "meta-wayland-surface-role-tablet-cursor.h" #include "meta-surface-actor-wayland.h" #include "meta-wayland-tablet.h" #include "meta-wayland-tablet-seat.h" #include "meta-wayland-tablet-tool.h" #include "backends/meta-input-settings-private.h" #include "backends/meta-logical-monitor.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #include #endif #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)) { MetaWaylandSurfaceRoleCursor *cursor_role = META_WAYLAND_SURFACE_ROLE_CURSOR (tool->cursor_surface->role); cursor = meta_wayland_surface_role_cursor_get_sprite (cursor_role); } else cursor = NULL; } else if (tool->current_tablet) cursor = 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) wl_list_remove (&tool->cursor_surface_destroy_listener.link); tool->cursor_surface = surface; if (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 (MetaCursorSprite *cursor_sprite, 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) meta_cursor_sprite_set_theme_scale (cursor_sprite, logical_monitor->scale); } 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_from_theme (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_signal_handler_disconnect (tool->default_sprite, tool->prepare_at_signal_id); 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_SURFACE_ROLE_TABLET_CURSOR, 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) { MetaWaylandSurfaceRoleCursor *cursor_role; cursor_role = META_WAYLAND_SURFACE_ROLE_CURSOR (surface->role); meta_wayland_surface_role_cursor_set_renderer (cursor_role, tool->cursor_renderer); meta_wayland_surface_role_cursor_set_hotspot (cursor_role, 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); clutter_actor_transform_stage_point (CLUTTER_ACTOR (meta_surface_actor_get_texture (surface->surface_actor)), xf, yf, &xf, &yf); *sx = wl_fixed_from_double (xf) / surface->scale; *sy = wl_fixed_from_double (yf) / surface->scale; } 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 = clutter_evdev_event_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) { GList *l; if (tool->focus_surface == surface) return TRUE; for (l = surface->subsurfaces; l; l = l->next) { MetaWaylandSurface *subsurface = l->data; 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)); } ukwm/src/wayland/meta-pointer-confinement-wayland.h0000664000175000017500000000305413233511035021424 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 */ ukwm/src/wayland/meta-wayland-wl-shell.h0000664000175000017500000000241313233511035017166 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef META_WAYLAND_WL_SHELL_H #define META_WAYLAND_WL_SHELL_H #include "wayland/meta-wayland-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, MetaWaylandSurfaceRoleShellSurface); void meta_wayland_wl_shell_init (MetaWaylandCompositor *compositor); #endif /* META_WAYLAND_WL_SHELL_H */ ukwm/src/wayland/meta-wayland-data-device-private.h0000664000175000017500000000314713233511035021262 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #ifndef META_WAYLAND_DATA_DEVICE_PRIVATE_H #define META_WAYLAND_DATA_DEVICE_PRIVATE_H #define META_TYPE_WAYLAND_DATA_SOURCE_WAYLAND (meta_wayland_data_source_wayland_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandDataSourceWayland, meta_wayland_data_source_wayland, META, WAYLAND_DATA_SOURCE_WAYLAND, MetaWaylandDataSource); #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, MetaWaylandDataSourceWayland); #endif /* META_WAYLAND_DATA_DEVICE_PRIVATE_H */ ukwm/src/wayland/meta-wayland-private.h0000664000175000017500000000375113233511035017117 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef META_WAYLAND_PRIVATE_H #define META_WAYLAND_PRIVATE_H #include #include #include #include "window-private.h" #include #include "meta-wayland.h" #include "meta-wayland-versions.h" #include "meta-wayland-surface.h" #include "meta-wayland-seat.h" #include "meta-wayland-pointer-gestures.h" #include "meta-wayland-tablet-manager.h" typedef struct _MetaXWaylandSelection MetaXWaylandSelection; 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; struct wl_client *client; struct wl_resource *xserver_resource; char *display_name; GCancellable *xserver_died_cancellable; GSubprocess *proc; GMainLoop *init_loop; MetaXWaylandSelection *selection_data; } MetaXWaylandManager; struct _MetaWaylandCompositor { struct wl_display *wayland_display; const char *display_name; GHashTable *outputs; struct wl_list frame_callbacks; MetaXWaylandManager xwayland_manager; MetaWaylandSeat *seat; MetaWaylandTabletManager *tablet_manager; }; #endif /* META_WAYLAND_PRIVATE_H */ ukwm/src/wayland/meta-wayland-tablet-pad-group.c0000664000175000017500000002733413233511035020612 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author: Carlos Garnacho */ #define _GNU_SOURCE #include "config.h" #include #include "tablet-unstable-v2-server-protocol.h" #include "meta-surface-actor-wayland.h" #include "meta-wayland-tablet-seat.h" #include "meta-wayland-tablet-pad.h" #include "meta-wayland-tablet-pad-group.h" #include "meta-wayland-tablet-pad-ring.h" #include "meta-wayland-tablet-pad-strip.h" #ifdef HAVE_NATIVE_BACKEND #include #include "backends/native/meta-backend-native.h" #endif 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) { MetaBackend *backend = meta_get_backend (); #ifdef HAVE_NATIVE_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 = clutter_evdev_input_device_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); } ukwm/src/wayland/meta-xwayland-private.h0000664000175000017500000000265113233511035017305 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef META_XWAYLAND_PRIVATE_H #define META_XWAYLAND_PRIVATE_H #include "meta-wayland-private.h" #include gboolean meta_xwayland_start (MetaXWaylandManager *manager, struct wl_display *display); void meta_xwayland_complete_init (void); void meta_xwayland_stop (MetaXWaylandManager *manager); /* wl_data_device/X11 selection interoperation */ void meta_xwayland_init_selection (void); void meta_xwayland_shutdown_selection (void); gboolean meta_xwayland_selection_handle_event (XEvent *xevent); const MetaWaylandDragDestFuncs * meta_xwayland_selection_get_drag_dest_funcs (void); #endif /* META_XWAYLAND_PRIVATE_H */ ukwm/src/wayland/meta-wayland-surface-role-tablet-cursor.h0000664000175000017500000000247313233511035022620 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef META_WAYLAND_SURFACE_ROLE_TABLET_CURSOR_H #define META_WAYLAND_SURFACE_ROLE_TABLET_CURSOR_H #include "meta-wayland-surface-role-cursor.h" #define META_TYPE_WAYLAND_SURFACE_ROLE_TABLET_CURSOR (meta_wayland_surface_role_tablet_cursor_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandSurfaceRoleTabletCursor, meta_wayland_surface_role_tablet_cursor, META, WAYLAND_SURFACE_ROLE_TABLET_CURSOR, MetaWaylandSurfaceRoleCursor); #endif /* META_WAYLAND_SURFACE_ROLE_TABLET_CURSOR_H */ ukwm/src/wayland/meta-wayland.c0000664000175000017500000003404413233511035015441 0ustar fengfeng/* * Wayland Support * * Copyright (C) 2012,2013 Intel Corporation * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "config.h" #include "meta-wayland.h" #include #include #include #include #include #include #include #include "meta-wayland-private.h" #include "meta-xwayland-private.h" #include "meta-wayland-region.h" #include "meta-wayland-seat.h" #include "meta-wayland-outputs.h" #include "meta-wayland-data-device.h" #include "meta-wayland-tablet-manager.h" #include "meta-wayland-xdg-foreign.h" #include "meta-wayland-dma-buf.h" #include "meta-wayland-inhibit-shortcuts.h" #include "meta-wayland-inhibit-shortcuts-dialog.h" static MetaWaylandCompositor _meta_wayland_compositor; static char *_display_name_override; MetaWaylandCompositor * meta_wayland_compositor_get_default (void) { return &_meta_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) { gint64 current_time = g_get_monotonic_time (); while (!wl_list_empty (&compositor->frame_callbacks)) { MetaWaylandFrameCallback *callback = wl_container_of (compositor->frame_callbacks.next, callback, link); wl_callback_send_done (callback->resource, current_time / 1000); wl_resource_destroy (callback->resource); } } /** * 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 Ukwm * 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_destroy_frame_callbacks (MetaWaylandCompositor *compositor, MetaWaylandSurface *surface) { MetaWaylandFrameCallback *callback, *next; wl_list_for_each_safe (callback, next, &compositor->frame_callbacks, link) { if (callback->surface == surface) wl_resource_destroy (callback->resource); } } static void set_gnome_env (const char *name, const char *value) { GDBusConnection *session_bus; GError *error = NULL; setenv (name, value, TRUE); session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); g_assert (session_bus); 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) { if (g_strcmp0 (g_dbus_error_get_remote_error (error), "org.gnome.SessionManager.NotInInitialization") != 0) meta_warning ("Failed to set environment variable %s for gnome-session: %s\n", name, error->message); 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) { memset (compositor, 0, sizeof (MetaWaylandCompositor)); wl_list_init (&compositor->frame_callbacks); } void meta_wayland_pre_clutter_init (void) { MetaWaylandCompositor *compositor = &_meta_wayland_compositor; meta_wayland_compositor_init (compositor); 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); } void meta_wayland_override_display_name (char *display_name) { g_clear_pointer (&_display_name_override, g_free); _display_name_override = g_strdup (display_name); } void meta_wayland_init (void) { 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_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 (); if (!meta_xwayland_start (&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); } set_gnome_env ("DISPLAY", meta_wayland_get_xwayland_display_name (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.display_name; } void meta_wayland_finalize (void) { MetaWaylandCompositor *compositor; compositor = meta_wayland_compositor_get_default (); meta_xwayland_stop (&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; if (clutter_input_device_get_device_type (source) != CLUTTER_KEYBOARD_DEVICE) return FALSE; /* 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); } ukwm/src/wayland/meta-wayland-data-device.h0000664000175000017500000001556313220600404017612 0ustar fengfeng/* * 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 "meta-wayland-types.h" #include "clutter/clutter.h" typedef struct _MetaWaylandDragGrab MetaWaylandDragGrab; typedef struct _MetaWaylandDataSourceFuncs MetaWaylandDataSourceFuncs; #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); 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); }; struct _MetaWaylandDataDevice { uint32_t selection_serial; uint32_t primary_serial; MetaWaylandDataSource *selection_data_source; MetaWaylandDataSource *dnd_data_source; MetaWaylandDataSource *primary_data_source; struct wl_listener selection_data_source_listener; struct wl_list resource_list; struct wl_list primary_resource_list; MetaWaylandDragGrab *current_grab; struct wl_client *focus_client; struct wl_signal selection_ownership_signal; struct wl_signal dnd_ownership_signal; struct wl_signal primary_ownership_signal; }; 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_set_primary (MetaWaylandDataDevice *data_device, MetaWaylandDataSource *source, guint32 serial); gboolean meta_wayland_data_source_add_mime_type (MetaWaylandDataSource *source, const gchar *mime_type); gboolean meta_wayland_data_source_has_mime_type (const MetaWaylandDataSource *source, const gchar *mime_type); struct wl_array * meta_wayland_data_source_get_mime_types (const MetaWaylandDataSource *source); gboolean meta_wayland_data_source_has_target (MetaWaylandDataSource *source); void meta_wayland_data_source_set_has_target (MetaWaylandDataSource *source, gboolean has_target); void meta_wayland_data_source_send (MetaWaylandDataSource *source, const gchar *mime_type, gint fd); void meta_wayland_data_source_notify_finish (MetaWaylandDataSource *source); uint32_t meta_wayland_data_source_get_actions (MetaWaylandDataSource *source); uint32_t meta_wayland_data_source_get_user_action (MetaWaylandDataSource *source); uint32_t meta_wayland_data_source_get_current_action (MetaWaylandDataSource *source); void meta_wayland_data_source_set_actions (MetaWaylandDataSource *source, uint32_t dnd_actions); void meta_wayland_data_source_set_current_action (MetaWaylandDataSource *source, uint32_t action); 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 */ ukwm/src/wayland/meta-xwayland-selection-private.h0000664000175000017500000000246113233511035021267 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 */ ukwm/src/wayland/meta-wayland-tablet-pad.c0000664000175000017500000004336313233511035017460 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author: Carlos Garnacho */ #define _GNU_SOURCE #include "config.h" #include #include #include #include "tablet-unstable-v2-server-protocol.h" #include "backends/meta-input-settings-private.h" #include "meta-surface-actor-wayland.h" #include "meta-wayland-private.h" #include "meta-wayland-tablet-seat.h" #include "meta-wayland-tablet.h" #include "meta-wayland-tablet-pad.h" #include "meta-wayland-tablet-pad-group.h" #include "meta-wayland-tablet-pad-ring.h" #include "meta-wayland-tablet-pad-strip.h" #ifdef HAVE_NATIVE_BACKEND #include #include "backends/native/meta-backend-native.h" #endif 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 = clutter_evdev_input_device_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) { MetaBackend *backend = meta_get_backend (); 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 = clutter_evdev_input_device_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); GList *l; zwp_tablet_pad_v2_send_path (resource, clutter_input_device_get_device_node (pad->device)); 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_TOOL_V2_BUTTON_STATE_PRESSED; else if (event->type == CLUTTER_PAD_BUTTON_RELEASE) button_state = ZWP_TABLET_TOOL_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); } ukwm/src/wayland/meta-wayland-gtk-shell.h0000664000175000017500000000172313233511035017334 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef META_WAYLAND_GTK_SHELL_H #define META_WAYLAND_GTK_SHELL_H #include "wayland/meta-wayland.h" void meta_wayland_gtk_shell_init (MetaWaylandCompositor *compositor); #endif /* META_WAYLAND_GTK_SHELL_H */ ukwm/src/wayland/meta-pointer-lock-wayland.h0000664000175000017500000000254713233511035020055 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 */ ukwm/src/wayland/meta-xwayland.h0000664000175000017500000000235113233511035015632 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_XWAYLAND_H #define META_XWAYLAND_H #include #include #include "wayland/meta-wayland-types.h" void meta_xwayland_handle_wl_surface_id (MetaWindow *window, guint32 surface_id); gboolean meta_xwayland_is_xwayland_surface (MetaWaylandSurface *surface); #endif /* META_XWAYLAND_H */ ukwm/src/wayland/meta-wayland-surface-role-cursor.c0000664000175000017500000003234313233511035021341 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "config.h" #include #include #include "meta-wayland-surface-role-cursor.h" #include "meta-wayland-buffer.h" #include "meta-xwayland.h" #include "screen-private.h" #include "meta-wayland-private.h" #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "core/boxes-private.h" typedef struct _MetaWaylandSurfaceRoleCursorPrivate MetaWaylandSurfaceRoleCursorPrivate; struct _MetaWaylandSurfaceRoleCursorPrivate { int hot_x; int hot_y; MetaCursorSprite *cursor_sprite; MetaCursorRenderer *cursor_renderer; MetaWaylandBuffer *buffer; struct wl_list frame_callbacks; gulong cursor_painted_handler_id; }; G_DEFINE_TYPE_WITH_PRIVATE (MetaWaylandSurfaceRoleCursor, meta_wayland_surface_role_cursor, META_TYPE_WAYLAND_SURFACE_ROLE) static void update_cursor_sprite_texture (MetaWaylandSurfaceRoleCursor *cursor_role) { MetaWaylandSurfaceRoleCursorPrivate *priv = meta_wayland_surface_role_cursor_get_instance_private (cursor_role); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (cursor_role)); MetaWaylandBuffer *buffer = meta_wayland_surface_get_buffer (surface); MetaCursorSprite *cursor_sprite = priv->cursor_sprite; g_return_if_fail (!buffer || buffer->texture); if (!priv->cursor_renderer || !cursor_sprite) return; if (buffer) { meta_cursor_sprite_set_texture (cursor_sprite, buffer->texture, priv->hot_x * surface->scale, priv->hot_y * surface->scale); if (priv->buffer) { struct wl_resource *buffer_resource; g_assert (priv->buffer == buffer); buffer_resource = buffer->resource; meta_cursor_renderer_realize_cursor_from_wl_buffer (priv->cursor_renderer, cursor_sprite, buffer_resource); meta_wayland_surface_unref_buffer_use_count (surface); g_clear_object (&priv->buffer); } } 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, MetaWaylandSurfaceRoleCursor *cursor_role) { MetaWaylandSurfaceRole *role = META_WAYLAND_SURFACE_ROLE (cursor_role); 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 cursor_surface_role_assigned (MetaWaylandSurfaceRole *surface_role) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandSurfaceRoleCursor *cursor_role = META_WAYLAND_SURFACE_ROLE_CURSOR (surface_role); MetaWaylandSurfaceRoleCursorPrivate *priv = meta_wayland_surface_role_cursor_get_instance_private (cursor_role); wl_list_insert_list (&priv->frame_callbacks, &surface->pending_frame_callback_list); wl_list_init (&surface->pending_frame_callback_list); } static void cursor_surface_role_pre_commit (MetaWaylandSurfaceRole *surface_role, MetaWaylandPendingState *pending) { MetaWaylandSurfaceRoleCursor *cursor_role = META_WAYLAND_SURFACE_ROLE_CURSOR (surface_role); MetaWaylandSurfaceRoleCursorPrivate *priv = meta_wayland_surface_role_cursor_get_instance_private (cursor_role); 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 cursor_surface_role_commit (MetaWaylandSurfaceRole *surface_role, MetaWaylandPendingState *pending) { MetaWaylandSurfaceRoleCursor *cursor_role = META_WAYLAND_SURFACE_ROLE_CURSOR (surface_role); MetaWaylandSurfaceRoleCursorPrivate *priv = meta_wayland_surface_role_cursor_get_instance_private (cursor_role); 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) update_cursor_sprite_texture (META_WAYLAND_SURFACE_ROLE_CURSOR (surface_role)); } static gboolean cursor_surface_role_is_on_logical_monitor (MetaWaylandSurfaceRole *role, MetaLogicalMonitor *logical_monitor) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (role); MetaWaylandSurfaceRoleCursor *cursor_role = META_WAYLAND_SURFACE_ROLE_CURSOR (surface->role); MetaWaylandSurfaceRoleCursorPrivate *priv = meta_wayland_surface_role_cursor_get_instance_private (cursor_role); ClutterRect rect; ClutterRect logical_monitor_rect; rect = meta_cursor_renderer_calculate_rect (priv->cursor_renderer, priv->cursor_sprite); logical_monitor_rect = meta_rectangle_to_clutter_rect (&logical_monitor->rect); return clutter_rect_intersection (&rect, &logical_monitor_rect, NULL); } static void cursor_surface_role_dispose (GObject *object) { MetaWaylandSurfaceRoleCursor *cursor_role = META_WAYLAND_SURFACE_ROLE_CURSOR (object); MetaWaylandSurfaceRoleCursorPrivate *priv = meta_wayland_surface_role_cursor_get_instance_private (cursor_role); 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_role); 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_surface_role_cursor_parent_class)->dispose (object); } static void cursor_surface_role_constructed (GObject *object) { MetaWaylandSurfaceRoleCursor *cursor_role = META_WAYLAND_SURFACE_ROLE_CURSOR (object); MetaWaylandSurfaceRoleCursorPrivate *priv = meta_wayland_surface_role_cursor_get_instance_private (cursor_role); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (cursor_role); 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); } } static void meta_wayland_surface_role_cursor_init (MetaWaylandSurfaceRoleCursor *role) { MetaWaylandSurfaceRoleCursorPrivate *priv = meta_wayland_surface_role_cursor_get_instance_private (role); priv->cursor_sprite = meta_cursor_sprite_new (); g_signal_connect_object (priv->cursor_sprite, "prepare-at", G_CALLBACK (cursor_sprite_prepare_at), role, 0); wl_list_init (&priv->frame_callbacks); } static void meta_wayland_surface_role_cursor_class_init (MetaWaylandSurfaceRoleCursorClass *klass) { MetaWaylandSurfaceRoleClass *surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); surface_role_class->assigned = cursor_surface_role_assigned; surface_role_class->pre_commit = cursor_surface_role_pre_commit; surface_role_class->commit = cursor_surface_role_commit; surface_role_class->is_on_logical_monitor = cursor_surface_role_is_on_logical_monitor; object_class->constructed = cursor_surface_role_constructed; object_class->dispose = cursor_surface_role_dispose; } MetaCursorSprite * meta_wayland_surface_role_cursor_get_sprite (MetaWaylandSurfaceRoleCursor *cursor_role) { MetaWaylandSurfaceRoleCursorPrivate *priv = meta_wayland_surface_role_cursor_get_instance_private (cursor_role); return priv->cursor_sprite; } void meta_wayland_surface_role_cursor_set_hotspot (MetaWaylandSurfaceRoleCursor *cursor_role, gint hotspot_x, gint hotspot_y) { MetaWaylandSurfaceRoleCursorPrivate *priv = meta_wayland_surface_role_cursor_get_instance_private (cursor_role); 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_role); } void meta_wayland_surface_role_cursor_get_hotspot (MetaWaylandSurfaceRoleCursor *cursor_role, gint *hotspot_x, gint *hotspot_y) { MetaWaylandSurfaceRoleCursorPrivate *priv = meta_wayland_surface_role_cursor_get_instance_private (cursor_role); 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, MetaWaylandSurfaceRoleCursor *cursor_role) { MetaWaylandSurfaceRoleCursorPrivate *priv = meta_wayland_surface_role_cursor_get_instance_private (cursor_role); guint32 time = (guint32) (g_get_monotonic_time () / 1000); if (displayed_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_surface_role_cursor_set_renderer (MetaWaylandSurfaceRoleCursor *cursor_role, MetaCursorRenderer *renderer) { MetaWaylandSurfaceRoleCursorPrivate *priv = meta_wayland_surface_role_cursor_get_instance_private (cursor_role); if (priv->cursor_renderer == renderer) return; if (priv->cursor_renderer) { g_signal_handler_disconnect (priv->cursor_renderer, priv->cursor_painted_handler_id); priv->cursor_painted_handler_id = 0; 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_role, 0); g_object_ref (renderer); } priv->cursor_renderer = renderer; update_cursor_sprite_texture (cursor_role); } MetaCursorRenderer * meta_wayland_surface_role_cursor_get_renderer (MetaWaylandSurfaceRoleCursor *cursor_role) { MetaWaylandSurfaceRoleCursorPrivate *priv = meta_wayland_surface_role_cursor_get_instance_private (cursor_role); return priv->cursor_renderer; } ukwm/src/wayland/meta-wayland-outputs.c0000664000175000017500000003276213233511035017167 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "meta-wayland-outputs.h" #include "meta-wayland-private.h" #include "backends/meta-logical-monitor.h" #include "meta-monitor-manager-private.h" #include enum { OUTPUT_DESTROYED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; G_DEFINE_TYPE (MetaWaylandOutput, meta_wayland_output, G_TYPE_OBJECT) 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 (); } 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 send_output_events (struct wl_resource *resource, MetaWaylandOutput *wayland_output, MetaLogicalMonitor *logical_monitor, gboolean need_all_events) { 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; 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) { 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. */ meta_monitor_get_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; if (need_all_events || old_logical_monitor->rect.width != logical_monitor->rect.width || old_logical_monitor->rect.height != logical_monitor->rect.height || old_refresh_rate != refresh_rate || old_mode_flags != mode_flags) { wl_output_send_mode (resource, mode_flags, logical_monitor->rect.width, logical_monitor->rect.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_done) wl_output_send_done (resource); } } 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; MetaMonitor *monitor; 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); 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, logical_monitor->rect.width, logical_monitor->rect.height, wayland_output->refresh_rate); send_output_events (resource, wayland_output, logical_monitor, TRUE); } 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); } static void wayland_output_update_for_output (MetaWaylandOutput *wayland_output, MetaLogicalMonitor *logical_monitor) { GList *iter; for (iter = wayland_output->resources; iter; iter = iter->next) { struct wl_resource *resource = iter->data; send_output_events (resource, wayland_output, logical_monitor, FALSE); } /* 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 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 (NULL, NULL, NULL, wayland_output_destroy_notify); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; MetaWaylandOutput *wayland_output; if (logical_monitor->winsys_id == 0) continue; wayland_output = g_hash_table_lookup (compositor->outputs, GSIZE_TO_POINTER (logical_monitor->winsys_id)); if (wayland_output) { g_hash_table_steal (compositor->outputs, GSIZE_TO_POINTER (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, GSIZE_TO_POINTER (logical_monitor->winsys_id), wayland_output); } g_hash_table_destroy (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); GList *l; wl_global_destroy (wayland_output->global); /* Make sure the wl_output destructor doesn't try to access MetaWaylandOutput * after we have freed it. */ 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); 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); } 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 (NULL, NULL, NULL, wayland_output_destroy_notify); compositor->outputs = meta_wayland_compositor_update_outputs (compositor, monitors); } ukwm/src/wayland/meta-wayland-xdg-foreign.c0000664000175000017500000003043213233511035017645 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 "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_signal_handler_disconnect (exported->surface, exported->surface_unmapped_handler_id); 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_IS_WAYLAND_XDG_SURFACE (surface->role) || !surface->window) { 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_SURFACE (surface->role)) return FALSE; if (!surface->window) 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_signal_handler_disconnect (imported->parent_of, imported->parent_of_unmapped_handler_id); imported->parent_of = surface; if (surface) { imported->parent_of_unmapped_handler_id = g_signal_connect (surface, "unmapped", G_CALLBACK (imported_parent_of_unmapped), imported); meta_window_set_transient_for (surface->window, imported->exported->surface->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_signal_handler_disconnect (imported->parent_of, imported->parent_of_unmapped_handler_id); window = imported->parent_of->window; 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)) { zxdg_imported_v1_send_destroyed (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; } ukwm/src/wayland/meta-wayland-keyboard.h0000664000175000017500000001204613233511035017242 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 #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; int keymap_fd; size_t keymap_size; char *keymap_area; } 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_serial; MetaWaylandXkbInfo xkb_info; enum xkb_state_component mods_changed; MetaWaylandKeyboardGrab *grab; MetaWaylandKeyboardGrab default_grab; GSettings *settings; GSettings *gsd_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 */ ukwm/src/wayland/meta-wayland-wl-shell.c0000664000175000017500000006061313233511035017167 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * */ #include "config.h" #include "wayland/meta-wayland-wl-shell.h" #include "core/window-private.h" #include "wayland/meta-wayland.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-wayland-versions.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 { MetaWaylandSurfaceRoleShellSurface 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; }; static void popup_surface_iface_init (MetaWaylandPopupSurfaceInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaWaylandWlShellSurface, meta_wayland_wl_shell_surface, META_TYPE_WAYLAND_SURFACE_ROLE_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; meta_wayland_compositor_destroy_frame_callbacks (surface->compositor, surface); 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; wl_shell_surface->state = state; if (surface->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 (surface->window); else meta_window_unmake_fullscreen (surface->window); if (state == META_WL_SHELL_SURFACE_STATE_MAXIMIZED) meta_window_maximize (surface->window, META_MAXIMIZE_BOTH); else meta_window_unmaximize (surface->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 (surface->window && parent_surf->window) 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 (surface->window && parent_surf->window) 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); g_clear_pointer (&wl_shell_surface->title, g_free); if (!g_utf8_validate (title, -1, NULL)) title = ""; wl_shell_surface->title = g_strdup (title); if (surface->window) meta_window_set_title (surface->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); 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_); if (surface->window) meta_window_set_wm_class (surface->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); meta_window_set_transient_for (surface->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 (surface->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); MetaWaylandSurface *parent; GList *l; surface->window = meta_window_wayland_new (meta_get_display (), surface); meta_wayland_surface_set_window (surface, surface->window); if (wl_shell_surface->title) meta_window_set_title (surface->window, wl_shell_surface->title); if (wl_shell_surface->wm_class) meta_window_set_wm_class (surface->window, wl_shell_surface->wm_class, wl_shell_surface->wm_class); parent = wl_shell_surface->parent_surface; if (parent && parent->window) sync_wl_shell_parent_relationship (surface, parent); for (l = wl_shell_surface->children; l; l = l->next) { MetaWaylandSurface *child = l->data; if (child->window) 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_commit (MetaWaylandSurfaceRole *surface_role, MetaWaylandPendingState *pending) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (surface_role); MetaWaylandSurfaceRoleClass *surface_role_class; MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWindow *window = surface->window; MetaRectangle geom = { 0 }; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_wl_shell_surface_parent_class); surface_role_class->commit (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_surface_destroy_window (surface); return; } if (!window) return; if (!pending->newly_attached) return; meta_wayland_surface_calculate_window_geometry (surface, &geom, 0, 0); meta_window_wayland_move_resize (window, NULL, geom, pending->dx, pending->dy); } 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 (MetaWaylandSurfaceRoleShellSurface *shell_surface_role, int new_x, int new_y, int new_width, int new_height, MetaWaylandSerial *sent_serial) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (shell_surface_role); if (!wl_shell_surface->resource) return; wl_shell_surface_send_configure (wl_shell_surface->resource, 0, new_width, new_height); } static void wl_shell_surface_role_managed (MetaWaylandSurfaceRoleShellSurface *shell_surface_role, MetaWindow *window) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (shell_surface_role); 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 (MetaWaylandSurfaceRoleShellSurface *shell_surface_role, guint32 serial) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (shell_surface_role); wl_shell_surface_send_ping (wl_shell_surface->resource, serial); } static void wl_shell_surface_role_close (MetaWaylandSurfaceRoleShellSurface *shell_surface_role) { /* 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); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (popup_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); wl_shell_surface->popup = NULL; meta_wayland_surface_destroy_window (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; MetaWaylandSurfaceRoleShellSurfaceClass *shell_surface_role_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->commit = wl_shell_surface_role_commit; surface_role_class->get_toplevel = wl_shell_surface_role_get_toplevel; shell_surface_role_class = META_WAYLAND_SURFACE_ROLE_SHELL_SURFACE_CLASS (klass); shell_surface_role_class->configure = wl_shell_surface_role_configure; shell_surface_role_class->managed = wl_shell_surface_role_managed; shell_surface_role_class->ping = wl_shell_surface_role_ping; shell_surface_role_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"); } ukwm/src/wayland/meta-wayland-versions.h0000664000175000017500000000407613233511035017316 0ustar fengfeng/* * Wayland Support * * Copyright (C) 2012,2013 Intel Corporation * 2013 Red Hat, Inc. * 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 ukwm-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_SHELL_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 2 #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 #endif ukwm/src/wayland/meta-window-wayland.c0000664000175000017500000007053113233511035016747 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "meta-window-wayland.h" #include #include #include /* for strerror () */ #include "window-private.h" #include "boxes-private.h" #include "stack-tracker.h" #include "meta-wayland-private.h" #include "meta-wayland-surface.h" #include "meta-wayland-xdg-shell.h" #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "compositor/meta-surface-actor-wayland.h" #include "backends/meta-backend-private.h" struct _MetaWindowWayland { MetaWindow parent; int geometry_scale; MetaWaylandSerial pending_configure_serial; gboolean has_pending_move; int pending_move_x; int pending_move_y; int last_sent_x; int last_sent_y; int last_sent_width; int last_sent_height; }; struct _MetaWindowWaylandClass { MetaWindowClass parent_class; }; G_DEFINE_TYPE (MetaWindowWayland, meta_window_wayland, META_TYPE_WINDOW) static int get_window_geometry_scale_for_logical_monitor (MetaLogicalMonitor *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 = get_window_geometry_scale_for_logical_monitor (window->monitor); meta_display_register_wayland_window (display, window); { meta_stack_tracker_record_add (window->screen->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->screen->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 (window->input) meta_display_set_input_focus_window (window->display, window, FALSE, timestamp); } static void surface_state_changed (MetaWindow *window) { MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); /* don't send notify when the window is being unmanaged */ if (window->unmanaging) return; meta_wayland_surface_configure_notify (window->surface, wl_window->last_sent_x, wl_window->last_sent_y, wl_window->last_sent_width, wl_window->last_sent_height, &wl_window->pending_configure_serial); } 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, int gravity, MetaRectangle unconstrained_rect, MetaRectangle constrained_rect, MetaMoveResizeFlags flags, MetaMoveResizeResultFlags *result) { MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); gboolean can_move_now; int configured_x; int configured_y; int configured_width; int configured_height; int geometry_scale; 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); configured_width = constrained_rect.width / geometry_scale; configured_height = constrained_rect.height / geometry_scale; /* 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_WAYLAND_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; } else { /* If the size changed, or the state changed, then we have to wait until * the client acks our configure before moving the window. */ if (constrained_rect.width != window->rect.width || constrained_rect.height != window->rect.height || (flags & META_MOVE_RESIZE_STATE_CHANGED)) { /* 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; meta_wayland_surface_configure_notify (window->surface, configured_x, configured_y, configured_width, configured_height, &wl_window->pending_configure_serial); /* We need to wait until the resize completes before we can move */ can_move_now = FALSE; } else { /* We're just moving the window, so we don't need to wait for a configure * and then ack to simply move the window. */ 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; if (can_move_now) { int new_x = constrained_rect.x; int new_y = constrained_rect.y; 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; } int new_buffer_x = new_x - window->custom_frame_extents.left; int 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; } } else { int new_x = constrained_rect.x; int new_y = constrained_rect.y; if (new_x != window->rect.x || new_y != window->rect.y) { wl_window->has_pending_move = TRUE; wl_window->pending_move_x = new_x; wl_window->pending_move_y = new_y; } } } 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, gboolean user_op) { 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, user_op); 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; } 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_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); /* The surface actor needs to update the scale recursively for itself and all * its subsurfaces */ surface = window->surface; if (surface) { MetaSurfaceActorWayland *actor = META_SURFACE_ACTOR_WAYLAND (surface->surface_actor); meta_surface_actor_wayland_sync_state_recursive (actor); } wl_window->geometry_scale = 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); surface_state_changed (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); } 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 void meta_window_wayland_class_init (MetaWindowWaylandClass *klass) { MetaWindowClass *window_class = META_WINDOW_CLASS (klass); 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; } MetaWindow * meta_window_wayland_new (MetaDisplay *display, MetaWaylandSurface *surface) { XWindowAttributes attrs = { 0 }; MetaScreen *scr = display->screen; 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; /* XXX: Note: In the Wayland case we currently still trap X errors while * creating a MetaWindow because we will still be making various redundant * X requests (passing a window xid of None) until we thoroughly audit all * the code to make sure it knows about non X based clients... */ meta_error_trap_push (display); /* Push a trap over all of window * creation, to reduce XSync() calls */ window = _meta_window_shared_new (display, scr, META_WINDOW_CLIENT_TYPE_WAYLAND, surface, None, WithdrawnState, META_COMP_EFFECT_CREATE, &attrs); window->can_ping = TRUE; meta_error_trap_pop (display); /* pop the XSync()-reducing trap */ return window; } static gboolean should_do_pending_move (MetaWindowWayland *wl_window, MetaWaylandSerial *acked_configure_serial) { if (!wl_window->has_pending_move) return FALSE; if (wl_window->pending_configure_serial.set) { /* If we're waiting for a configure and this isn't an ACK for * any configure, then fizzle it out. */ if (!acked_configure_serial->set) return FALSE; /* If we're waiting for a configure and this isn't an ACK for * the configure we're waiting for, then fizzle it out. */ if (acked_configure_serial->value != wl_window->pending_configure_serial.value) return FALSE; } return TRUE; } int meta_window_wayland_get_geometry_scale (MetaWindow *window) { return get_window_geometry_scale_for_logical_monitor (window->monitor); } /** * meta_window_move_resize_wayland: * * Complete a resize operation from a wayland client. */ void meta_window_wayland_move_resize (MetaWindow *window, MetaWaylandSerial *acked_configure_serial, MetaRectangle new_geom, int dx, int dy) { MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); int geometry_scale; int gravity; MetaRectangle rect; MetaMoveResizeFlags flags; /* 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 *= geometry_scale; 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_RESIZE; /* x/y are ignored when we're doing interactive resizing */ if (!meta_grab_op_is_resizing (window->display->grab_op)) { if (wl_window->has_pending_move && should_do_pending_move (wl_window, acked_configure_serial)) { rect.x = wl_window->pending_move_x; rect.y = wl_window->pending_move_y; wl_window->has_pending_move = FALSE; flags |= META_MOVE_RESIZE_MOVE_ACTION; } else { rect.x = window->rect.x; rect.y = window->rect.y; } if (dx != 0 || dy != 0) { rect.x += dx; rect.y += dy; flags |= META_MOVE_RESIZE_MOVE_ACTION; } } wl_window->pending_configure_serial.set = 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; gravity = meta_resize_gravity_from_grab_op (window->display->grab_op); meta_window_move_resize_internal (window, flags, gravity, rect); } 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) { g_clear_pointer (&window->placement_rule, g_free); window->placement_rule = g_new0 (MetaPlacementRule, 1); *window->placement_rule = *placement_rule; window->unconstrained_rect.width = placement_rule->width; window->unconstrained_rect.height = placement_rule->height; meta_window_force_placement (window); } 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); } ukwm/src/wayland/meta-wayland-pointer-constraints.h0000664000175000017500000000342413233511035021467 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #ifndef META_WAYLAND_POINTER_CONSTRAINTS_H #define META_WAYLAND_POINTER_CONSTRAINTS_H #include "meta-wayland-types.h" #include "meta/window.h" #include #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 */ ukwm/src/wayland/meta-wayland-pointer-gesture-pinch.c0000664000175000017500000001311413233511035021665 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author: Carlos Garnacho */ #include "config.h" #include #include "meta-wayland-pointer-gesture-pinch.h" #include "meta-wayland-pointer.h" #include "meta-wayland-seat.h" #include "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)); } ukwm/src/wayland/meta-wayland-gtk-shell.c0000664000175000017500000003042713233511035017332 0ustar fengfeng/* * Wayland Support * * Copyright (C) 2012,2013 Intel Corporation * 2013-2016 Red Hat, Inc. * 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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; 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_signal_handler_disconnect (gtk_surface->surface, gtk_surface->configure_handler_id); } 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; /* Broken client, let it die instead of us */ if (!surface->window) { meta_warning ("meta-wayland-surface: set_dbus_properties called with invalid window!\n"); return; } meta_window_set_gtk_dbus_properties (surface->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; if (gtk_surface->is_modal) return; gtk_surface->is_modal = TRUE; meta_window_set_type (surface->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; if (!gtk_surface->is_modal) return; gtk_surface->is_modal = FALSE; meta_window_set_type (surface->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 = surface->window; if (!window) return; meta_window_activate_full (window, timestamp, META_CLIENT_TYPE_APPLICATION, NULL); } 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, }; 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; /* Top */ if (window->edge_constraints[0] != META_EDGE_CONSTRAINT_MONITOR) { s = wl_array_add (states, sizeof *s); *s = GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_TOP; } /* Right */ if (window->edge_constraints[1] != META_EDGE_CONSTRAINT_MONITOR) { s = wl_array_add (states, sizeof *s); *s = GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_RIGHT; } /* Bottom */ if (window->edge_constraints[2] != META_EDGE_CONSTRAINT_MONITOR) { s = wl_array_add (states, sizeof *s); *s = GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_BOTTOM; } /* Left */ if (window->edge_constraints[3] != 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 fill_states (struct wl_array *states, MetaWindow *window, struct wl_resource *resource) { uint32_t *s; guint 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)) { s = wl_array_add (states, sizeof *s); *s = GTK_SURFACE1_STATE_TILED; } if (version >= GTK_SURFACE1_STATE_TILED_TOP_SINCE_VERSION && window->edge_constraints[0] != META_EDGE_CONSTRAINT_NONE) { s = wl_array_add (states, sizeof *s); *s = GTK_SURFACE1_STATE_TILED_TOP; } if (version >= GTK_SURFACE1_STATE_TILED_RIGHT_SINCE_VERSION && window->edge_constraints[1] != META_EDGE_CONSTRAINT_NONE) { s = wl_array_add (states, sizeof *s); *s = GTK_SURFACE1_STATE_TILED_RIGHT; } if (version >= GTK_SURFACE1_STATE_TILED_BOTTOM_SINCE_VERSION && window->edge_constraints[2] != META_EDGE_CONSTRAINT_NONE) { s = wl_array_add (states, sizeof *s); *s = GTK_SURFACE1_STATE_TILED_BOTTOM; } if (version >= GTK_SURFACE1_STATE_TILED_LEFT_SINCE_VERSION && window->edge_constraints[3] != META_EDGE_CONSTRAINT_NONE) { s = wl_array_add (states, sizeof *s); *s = 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) { send_configure (gtk_surface, surface->window); if (wl_resource_get_version (gtk_surface->resource) >= GTK_SURFACE1_CONFIGURE_EDGES_SINCE_VERSION) send_configure_edges (gtk_surface, 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) { #if 0 MetaDisplay *display; display = meta_get_display (); meta_startup_notification_remove_sequence (display->startup_notification, startup_id); #else /* HACK: MetaScreen::startup-sequence-changed is currently tied to (lib)startup-notification, which means it only works on X11; so for now, always go through XWayland, even for wayland clients */ gdk_x11_display_broadcast_startup_message (gdk_display_get_default (), "remove", "ID", startup_id, NULL); #endif } 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; if (!surface->window) return; meta_bell_notify (display, surface->window); } else { meta_bell_notify (display, NULL); } } 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, }; static void bind_gtk_shell (struct wl_client *client, void *data, guint32 version, guint32 id) { struct wl_resource *resource; uint32_t capabilities = 0; resource = wl_resource_create (client, >k_shell1_interface, version, id); wl_resource_set_implementation (resource, &meta_wayland_gtk_shell_interface, data, NULL); if (!meta_prefs_get_show_fallback_app_menu ()) capabilities = GTK_SHELL1_CAPABILITY_GLOBAL_APP_MENU; gtk_shell1_send_capabilities (resource, capabilities); } void meta_wayland_gtk_shell_init (MetaWaylandCompositor *compositor) { quark_gtk_surface_data = g_quark_from_static_string ("-meta-wayland-gtk-shell-surface-data"); if (wl_global_create (compositor->wayland_display, >k_shell1_interface, META_GTK_SHELL1_VERSION, compositor, bind_gtk_shell) == NULL) g_error ("Failed to register a global gtk-shell object"); } ukwm/src/wayland/meta-wayland-surface.c0000664000175000017500000022525513233511035017075 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "config.h" #include "meta-wayland-surface.h" #include #include #include #include #include #include #include "meta-wayland-private.h" #include "meta-xwayland-private.h" #include "meta-wayland-buffer.h" #include "meta-wayland-region.h" #include "meta-wayland-seat.h" #include "meta-wayland-keyboard.h" #include "meta-wayland-pointer.h" #include "meta-wayland-data-device.h" #include "meta-wayland-outputs.h" #include "meta-wayland-xdg-shell.h" #include "meta-wayland-wl-shell.h" #include "meta-wayland-gtk-shell.h" #include "meta-cursor-tracker-private.h" #include "display-private.h" #include "window-private.h" #include "meta-window-wayland.h" #include "compositor/region-utils.h" #include "compositor/meta-shaped-texture-private.h" #include "meta-surface-actor.h" #include "meta-surface-actor-wayland.h" #include "meta-xwayland-private.h" enum { PENDING_STATE_SIGNAL_APPLIED, PENDING_STATE_SIGNAL_LAST_SIGNAL }; enum { SURFACE_ROLE_PROP_0, SURFACE_ROLE_PROP_SURFACE, }; static guint pending_state_signals[PENDING_STATE_SIGNAL_LAST_SIGNAL]; typedef struct _MetaWaylandSurfaceRolePrivate { MetaWaylandSurface *surface; } MetaWaylandSurfaceRolePrivate; typedef enum { META_WAYLAND_SUBSURFACE_PLACEMENT_ABOVE, META_WAYLAND_SUBSURFACE_PLACEMENT_BELOW } MetaWaylandSubsurfacePlacement; typedef struct { MetaWaylandSubsurfacePlacement placement; MetaWaylandSurface *sibling; struct wl_listener sibling_destroy_listener; } MetaWaylandSubsurfacePlacementOp; G_DEFINE_TYPE (MetaWaylandSurface, meta_wayland_surface, G_TYPE_OBJECT); G_DEFINE_TYPE_WITH_PRIVATE (MetaWaylandSurfaceRole, meta_wayland_surface_role, G_TYPE_OBJECT); G_DEFINE_TYPE (MetaWaylandSurfaceRoleActorSurface, meta_wayland_surface_role_actor_surface, META_TYPE_WAYLAND_SURFACE_ROLE); G_DEFINE_TYPE (MetaWaylandSurfaceRoleShellSurface, meta_wayland_surface_role_shell_surface, META_TYPE_WAYLAND_SURFACE_ROLE_ACTOR_SURFACE); G_DEFINE_TYPE (MetaWaylandPendingState, meta_wayland_pending_state, G_TYPE_OBJECT); struct _MetaWaylandSurfaceRoleSubsurface { MetaWaylandSurfaceRoleActorSurface parent; }; G_DEFINE_TYPE (MetaWaylandSurfaceRoleSubsurface, meta_wayland_surface_role_subsurface, META_TYPE_WAYLAND_SURFACE_ROLE_ACTOR_SURFACE); struct _MetaWaylandSurfaceRoleDND { MetaWaylandSurfaceRole parent; }; G_DEFINE_TYPE (MetaWaylandSurfaceRoleDND, meta_wayland_surface_role_dnd, META_TYPE_WAYLAND_SURFACE_ROLE); enum { SURFACE_DESTROY, SURFACE_UNMAPPED, SURFACE_CONFIGURE, SURFACE_SHORTCUTS_INHIBITED, SURFACE_SHORTCUTS_RESTORED, 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_commit (MetaWaylandSurfaceRole *surface_role, MetaWaylandPendingState *pending); static void meta_wayland_surface_role_commit (MetaWaylandSurfaceRole *surface_role, MetaWaylandPendingState *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 meta_wayland_surface_role_shell_surface_configure (MetaWaylandSurfaceRoleShellSurface *shell_surface_role, int new_x, int new_y, int new_width, int new_height, MetaWaylandSerial *sent_serial); static void meta_wayland_surface_role_shell_surface_ping (MetaWaylandSurfaceRoleShellSurface *shell_surface_role, uint32_t serial); static void meta_wayland_surface_role_shell_surface_close (MetaWaylandSurfaceRoleShellSurface *shell_surface_role); static void meta_wayland_surface_role_shell_surface_managed (MetaWaylandSurfaceRoleShellSurface *shell_surface_role, MetaWindow *window); static void surface_actor_mapped_notify (MetaSurfaceActorWayland *surface_actor, GParamSpec *pspec, MetaWaylandSurface *surface); static void surface_actor_allocation_notify (MetaSurfaceActorWayland *surface_actor, GParamSpec *pspec, MetaWaylandSurface *surface); static void surface_actor_position_notify (MetaSurfaceActorWayland *surface_actor, GParamSpec *pspec, MetaWaylandSurface *surface); static void window_position_changed (MetaWindow *window, MetaWaylandSurface *surface); static void unset_param_value (GParameter *param) { g_value_unset (¶m->value); } static GArray * role_assignment_valist_to_params (GType role_type, const char *first_property_name, va_list var_args) { GObjectClass *object_class; const char *property_name = first_property_name; GArray *params; object_class = g_type_class_ref (role_type); params = g_array_new (FALSE, FALSE, sizeof (GParameter)); g_array_set_clear_func (params, (GDestroyNotify) unset_param_value); while (property_name) { GParameter param = { .name = property_name, .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 (¶m.value, ptype, var_args, 0, &error); g_assert (!error); g_array_append_val (params, param); property_name = va_arg (var_args, const char *); } g_type_class_unref (object_class); return params; } 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 *params; GParameter param; va_start (var_args, first_property_name); params = role_assignment_valist_to_params (role_type, first_property_name, var_args); va_end (var_args); param = (GParameter) { .name = "surface", .value = G_VALUE_INIT }; g_value_init (¶m.value, META_TYPE_WAYLAND_SURFACE); g_value_set_object (¶m.value, surface); g_array_append_val (params, param); surface->role = g_object_newv (role_type, params->len, (GParameter *) params->data); g_array_unref (params); } 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 void surface_process_damage (MetaWaylandSurface *surface, cairo_region_t *surface_region, cairo_region_t *buffer_region) { MetaWaylandBuffer *buffer = surface->buffer_ref.buffer; unsigned int buffer_width; unsigned int buffer_height; cairo_rectangle_int_t surface_rect; cairo_region_t *scaled_region; int i, n_rectangles; /* 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; /* 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). */ buffer_width = cogl_texture_get_width (buffer->texture); buffer_height = cogl_texture_get_height (buffer->texture); surface_rect = (cairo_rectangle_int_t) { .width = buffer_width / surface->scale, .height = buffer_height / surface->scale, }; 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); /* Now add the buffer damage on top of the scaled damage region, as buffer * damage is already in that scale. */ cairo_region_union (scaled_region, buffer_region); /* First update the buffer. */ meta_wayland_buffer_process_damage (buffer, scaled_region); /* Now damage the actor. The actor expects damage in the unscaled texture * coordinate space, i.e. same as the buffer. */ /* XXX: Should this be a signal / callback on MetaWaylandBuffer instead? */ n_rectangles = cairo_region_num_rectangles (scaled_region); for (i = 0; i < n_rectangles; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (scaled_region, i, &rect); meta_surface_actor_process_damage (surface->surface_actor, rect.x, rect.y, rect.width, rect.height); } cairo_region_destroy (scaled_region); } void meta_wayland_surface_queue_pending_state_frame_callbacks (MetaWaylandSurface *surface, MetaWaylandPendingState *pending) { wl_list_insert_list (&surface->compositor->frame_callbacks, &pending->frame_callback_list); wl_list_init (&pending->frame_callback_list); } static void dnd_surface_commit (MetaWaylandSurfaceRole *surface_role, MetaWaylandPendingState *pending) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); meta_wayland_surface_queue_pending_state_frame_callbacks (surface, pending); } void meta_wayland_surface_calculate_window_geometry (MetaWaylandSurface *surface, MetaRectangle *total_geometry, float parent_x, float parent_y) { MetaSurfaceActorWayland *surface_actor = META_SURFACE_ACTOR_WAYLAND (surface->surface_actor); MetaRectangle subsurface_rect; MetaRectangle geom; GList *l; /* Unmapped surfaces don't count. */ if (!CLUTTER_ACTOR_IS_VISIBLE (CLUTTER_ACTOR (surface_actor))) return; if (!surface->buffer_ref.buffer) return; meta_surface_actor_wayland_get_subsurface_rect (surface_actor, &subsurface_rect); geom.x = parent_x + subsurface_rect.x; geom.y = parent_x + subsurface_rect.y; geom.width = subsurface_rect.width; geom.height = subsurface_rect.height; meta_rectangle_union (total_geometry, &geom, total_geometry); for (l = surface->subsurfaces; l != NULL; l = l->next) { MetaWaylandSurface *subsurface = l->data; meta_wayland_surface_calculate_window_geometry (subsurface, total_geometry, subsurface_rect.x, subsurface_rect.y); } } void meta_wayland_surface_destroy_window (MetaWaylandSurface *surface) { if (surface->window) { MetaDisplay *display = meta_get_display (); guint32 timestamp = meta_display_get_current_time_roundtrip (display); meta_window_unmanage (surface->window, timestamp); } g_assert (surface->window == NULL); } 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 queue_surface_actor_frame_callbacks (MetaWaylandSurface *surface, MetaWaylandPendingState *pending) { MetaSurfaceActorWayland *surface_actor = META_SURFACE_ACTOR_WAYLAND (surface->surface_actor); meta_surface_actor_wayland_add_frame_callbacks (surface_actor, &pending->frame_callback_list); wl_list_init (&pending->frame_callback_list); } static void pending_buffer_resource_destroyed (MetaWaylandBuffer *buffer, MetaWaylandPendingState *pending) { g_signal_handler_disconnect (buffer, pending->buffer_destroy_handler_id); pending->buffer = NULL; } static void pending_state_init (MetaWaylandPendingState *state) { state->newly_attached = FALSE; state->buffer = NULL; 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_new_min_size = FALSE; state->has_new_max_size = FALSE; } static void pending_state_destroy (MetaWaylandPendingState *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_signal_handler_disconnect (state->buffer, state->buffer_destroy_handler_id); wl_list_for_each_safe (cb, next, &state->frame_callback_list, link) wl_resource_destroy (cb->resource); } static void pending_state_reset (MetaWaylandPendingState *state) { pending_state_destroy (state); pending_state_init (state); } static void move_pending_state (MetaWaylandPendingState *from, MetaWaylandPendingState *to) { if (from->buffer) g_signal_handler_disconnect (from->buffer, from->buffer_destroy_handler_id); to->newly_attached = from->newly_attached; to->buffer = from->buffer; to->dx = from->dx; to->dy = from->dy; to->scale = from->scale; to->surface_damage = from->surface_damage; to->buffer_damage = from->buffer_damage; to->input_region = from->input_region; to->input_region_set = from->input_region_set; to->opaque_region = from->opaque_region; to->opaque_region_set = from->opaque_region_set; to->new_geometry = from->new_geometry; to->has_new_geometry = from->has_new_geometry; to->has_new_min_size = from->has_new_min_size; to->new_min_width = from->new_min_width; to->new_min_height = from->new_min_height; to->has_new_max_size = from->has_new_max_size; to->new_max_width = from->new_max_width; to->new_max_height = from->new_max_height; wl_list_init (&to->frame_callback_list); wl_list_insert_list (&to->frame_callback_list, &from->frame_callback_list); if (to->buffer) { to->buffer_destroy_handler_id = g_signal_connect (to->buffer, "resource-destroyed", G_CALLBACK (pending_buffer_resource_destroyed), to); } pending_state_init (from); } static void meta_wayland_pending_state_finalize (GObject *object) { MetaWaylandPendingState *state = META_WAYLAND_PENDING_STATE (object); pending_state_destroy (state); G_OBJECT_CLASS (meta_wayland_pending_state_parent_class)->finalize (object); } static void meta_wayland_pending_state_init (MetaWaylandPendingState *state) { pending_state_init (state); } static void meta_wayland_pending_state_class_init (MetaWaylandPendingStateClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_wayland_pending_state_finalize; pending_state_signals[PENDING_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 subsurface_role_commit (MetaWaylandSurfaceRole *surface_role, MetaWaylandPendingState *pending) { MetaWaylandSurfaceRoleClass *surface_role_class; MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaSurfaceActorWayland *surface_actor = META_SURFACE_ACTOR_WAYLAND (surface->surface_actor); surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_surface_role_subsurface_parent_class); surface_role_class->commit (surface_role, pending); if (surface->buffer_ref.buffer != NULL) clutter_actor_show (CLUTTER_ACTOR (surface_actor)); else clutter_actor_hide (CLUTTER_ACTOR (surface_actor)); } static MetaWaylandSurface * subsurface_role_get_toplevel (MetaWaylandSurfaceRole *surface_role) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandSurface *parent = surface->sub.parent; if (parent && parent->role) return meta_wayland_surface_role_get_toplevel (parent->role); else return NULL; } /* A non-subsurface is always desynchronized. * * A subsurface is effectively synchronized if either its parent is * synchronized or itself is in synchronized mode. */ static gboolean is_surface_effectively_synchronized (MetaWaylandSurface *surface) { if (surface->wl_subsurface == NULL) { return FALSE; } else { if (surface->sub.synchronous) return TRUE; else return is_surface_effectively_synchronized (surface->sub.parent); } } static void apply_pending_state (MetaWaylandSurface *surface, MetaWaylandPendingState *pending); static void parent_surface_state_applied (gpointer data, gpointer user_data) { MetaWaylandSurface *surface = data; 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 (surface->sub.pending_placement_ops) { GSList *it; MetaWaylandSurface *parent = surface->sub.parent; ClutterActor *parent_actor = clutter_actor_get_parent (CLUTTER_ACTOR (parent->surface_actor)); ClutterActor *surface_actor = CLUTTER_ACTOR (surface->surface_actor); for (it = surface->sub.pending_placement_ops; it; it = it->next) { MetaWaylandSubsurfacePlacementOp *op = it->data; ClutterActor *sibling_actor; if (!op->sibling) { g_slice_free (MetaWaylandSubsurfacePlacementOp, op); continue; } sibling_actor = CLUTTER_ACTOR (op->sibling->surface_actor); switch (op->placement) { case META_WAYLAND_SUBSURFACE_PLACEMENT_ABOVE: clutter_actor_set_child_above_sibling (parent_actor, surface_actor, sibling_actor); break; case META_WAYLAND_SUBSURFACE_PLACEMENT_BELOW: clutter_actor_set_child_below_sibling (parent_actor, surface_actor, sibling_actor); break; } wl_list_remove (&op->sibling_destroy_listener.link); g_slice_free (MetaWaylandSubsurfacePlacementOp, op); } g_slist_free (surface->sub.pending_placement_ops); surface->sub.pending_placement_ops = NULL; } if (is_surface_effectively_synchronized (surface)) apply_pending_state (surface, surface->sub.pending); meta_surface_actor_wayland_sync_subsurface_state ( META_SURFACE_ACTOR_WAYLAND (surface->surface_actor)); } static void apply_pending_state (MetaWaylandSurface *surface, MetaWaylandPendingState *pending) { if (surface->role) { meta_wayland_surface_role_pre_commit (surface->role, pending); } else { if (pending->newly_attached && surface->unassigned.buffer) { meta_wayland_surface_unref_buffer_use_count (surface); g_clear_object (&surface->unassigned.buffer); } } if (pending->newly_attached) { gboolean switched_buffer; if (!surface->buffer_ref.buffer && surface->window) meta_window_queue (surface->window, META_QUEUE_CALC_SHOWING); /* 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); switched_buffer = g_set_object (&surface->buffer_ref.buffer, pending->buffer); if (pending->buffer) meta_wayland_surface_ref_buffer_use_count (surface); if (pending->buffer) { GError *error = NULL; if (!meta_wayland_buffer_attach (pending->buffer, &error)) { g_warning ("Could not import pending buffer: %s", error->message); wl_resource_post_error (surface->resource, WL_DISPLAY_ERROR_NO_MEMORY, "Failed to create a texture for surface %i: %s", wl_resource_get_id (surface->resource), error->message); g_error_free (error); goto cleanup; } if (switched_buffer) { MetaShapedTexture *stex; CoglTexture *texture; CoglSnippet *snippet; gboolean is_y_inverted; stex = meta_surface_actor_get_texture (surface->surface_actor); texture = meta_wayland_buffer_get_texture (pending->buffer); snippet = meta_wayland_buffer_create_snippet (pending->buffer); is_y_inverted = meta_wayland_buffer_is_y_inverted (pending->buffer); meta_shaped_texture_set_texture (stex, texture); meta_shaped_texture_set_snippet (stex, snippet); meta_shaped_texture_set_is_y_inverted (stex, is_y_inverted); g_clear_pointer (&snippet, cogl_object_unref); } } /* 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 = (pending->buffer && !wl_shm_buffer_get (pending->buffer->resource)); } if (pending->scale > 0) surface->scale = pending->scale; if (!cairo_region_is_empty (pending->surface_damage) || !cairo_region_is_empty (pending->buffer_damage)) surface_process_damage (surface, pending->surface_damage, pending->buffer_damage); surface->offset_x += pending->dx; surface->offset_y += pending->dy; if (pending->opaque_region_set) { if (surface->opaque_region) cairo_region_destroy (surface->opaque_region); if (pending->opaque_region) surface->opaque_region = cairo_region_reference (pending->opaque_region); else surface->opaque_region = NULL; } if (pending->input_region_set) { if (surface->input_region) cairo_region_destroy (surface->input_region); if (pending->input_region) surface->input_region = cairo_region_reference (pending->input_region); else surface->input_region = NULL; } if (surface->role) { meta_wayland_surface_role_commit (surface->role, pending); g_assert (wl_list_empty (&pending->frame_callback_list)); } else { /* Since there is no role assigned to the surface yet, keep frame * callbacks queued until a role is assigned and we know how * the surface will be drawn. */ wl_list_insert_list (&surface->pending_frame_callback_list, &pending->frame_callback_list); wl_list_init (&pending->frame_callback_list); if (pending->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); } } 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 (pending->newly_attached && !surface->buffer_held && surface->buffer_ref.buffer) meta_wayland_surface_unref_buffer_use_count (surface); g_signal_emit (pending, pending_state_signals[PENDING_STATE_SIGNAL_APPLIED], 0); pending_state_reset (pending); g_list_foreach (surface->subsurfaces, parent_surface_state_applied, NULL); } static void meta_wayland_surface_commit (MetaWaylandSurface *surface) { /* * 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 (is_surface_effectively_synchronized (surface)) move_pending_state (surface->pending, surface->sub.pending); else apply_pending_state (surface, surface->pending); } 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); 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->buffer) { g_signal_handler_disconnect (surface->pending->buffer, surface->pending->buffer_destroy_handler_id); } surface->pending->newly_attached = TRUE; surface->pending->buffer = buffer; surface->pending->dx = dx; surface->pending->dy = dy; if (buffer) { surface->pending->buffer_destroy_handler_id = g_signal_connect (buffer, "resource-destroyed", G_CALLBACK (pending_buffer_resource_destroyed), surface->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); 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 (surface->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); /* 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 (surface->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); /* X11 unmanaged window */ if (!surface) return; g_clear_pointer (&surface->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); surface->pending->opaque_region = cairo_region_copy (cr_region); } surface->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); /* X11 unmanaged window */ if (!surface) return; g_clear_pointer (&surface->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); surface->pending->input_region = cairo_region_copy (cr_region); } surface->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 void wl_surface_set_buffer_transform (struct wl_client *client, struct wl_resource *resource, int32_t transform) { g_warning ("TODO: support set_buffer_transform request"); } 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); if (scale > 0) surface->pending->scale = scale; else g_warning ("Trying to set invalid buffer_scale of %d\n", 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); 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 (surface->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 gboolean surface_should_be_reactive (MetaWaylandSurface *surface) { /* If we have a toplevel window, we should be reactive */ if (surface->window) return TRUE; /* If we're a subsurface, we should be reactive */ if (surface->wl_subsurface) return TRUE; return FALSE; } static void sync_reactive (MetaWaylandSurface *surface) { clutter_actor_set_reactive (CLUTTER_ACTOR (surface->surface_actor), surface_should_be_reactive (surface)); } static void sync_drag_dest_funcs (MetaWaylandSurface *surface) { if (surface->window && surface->window->client_type == META_WINDOW_CLIENT_TYPE_X11) surface->dnd.funcs = meta_xwayland_selection_get_drag_dest_funcs (); else surface->dnd.funcs = meta_wayland_data_device_get_drag_dest_funcs (); } 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 gboolean actor_surface_is_on_logical_monitor (MetaWaylandSurfaceRole *surface_role, MetaLogicalMonitor *logical_monitor) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaSurfaceActorWayland *actor = META_SURFACE_ACTOR_WAYLAND (surface->surface_actor); return meta_surface_actor_wayland_is_on_monitor (actor, logical_monitor); } 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); } static void meta_wayland_surface_update_outputs_recursively (MetaWaylandSurface *surface) { GList *l; meta_wayland_surface_update_outputs (surface); for (l = surface->subsurfaces; l != NULL; l = l->next) meta_wayland_surface_update_outputs_recursively (l->data); } void meta_wayland_surface_set_window (MetaWaylandSurface *surface, MetaWindow *window) { gboolean was_unmapped = surface->window && !window; if (surface->window == window) return; if (surface->window) { g_signal_handlers_disconnect_by_func (surface->window, window_position_changed, surface); } surface->window = window; sync_reactive (surface); sync_drag_dest_funcs (surface); if (was_unmapped) g_signal_emit (surface, surface_signals[SURFACE_UNMAPPED], 0); if (window) { g_signal_connect_object (window, "position-changed", G_CALLBACK (window_position_changed), surface, 0); } } 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_signal_handlers_disconnect_by_func (surface->surface_actor, surface_actor_mapped_notify, surface); g_signal_handlers_disconnect_by_func (surface->surface_actor, surface_actor_allocation_notify, surface); g_signal_handlers_disconnect_by_func (surface->surface_actor, surface_actor_position_notify, surface); g_clear_object (&surface->role); /* If we still have a window at the time of destruction, that means that * the client is disconnecting, as the resources are destroyed in a random * order. Simply destroy the window in this case. */ if (surface->window) meta_wayland_surface_destroy_window (surface); 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_object (&surface->buffer_ref.buffer); g_clear_object (&surface->pending); if (surface->opaque_region) cairo_region_destroy (surface->opaque_region); if (surface->input_region) cairo_region_destroy (surface->input_region); g_object_unref (surface->surface_actor); meta_wayland_compositor_destroy_frame_callbacks (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->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); g_hash_table_destroy (surface->shortcut_inhibited_seats); g_object_unref (surface); meta_wayland_compositor_repick (compositor); } static void surface_actor_mapped_notify (MetaSurfaceActorWayland *surface_actor, GParamSpec *pspec, MetaWaylandSurface *surface) { meta_wayland_surface_update_outputs_recursively (surface); } static void surface_actor_allocation_notify (MetaSurfaceActorWayland *surface_actor, GParamSpec *pspec, MetaWaylandSurface *surface) { meta_wayland_surface_update_outputs_recursively (surface); } static void surface_actor_position_notify (MetaSurfaceActorWayland *surface_actor, GParamSpec *pspec, MetaWaylandSurface *surface) { meta_wayland_surface_update_outputs_recursively (surface); } static void window_position_changed (MetaWindow *window, MetaWaylandSurface *surface) { meta_wayland_surface_update_outputs_recursively (surface); } 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); surface->compositor = compositor; surface->scale = 1; surface->resource = wl_resource_create (client, &wl_surface_interface, wl_resource_get_version (compositor_resource), id); wl_resource_set_implementation (surface->resource, &meta_wayland_wl_surface_interface, surface, wl_surface_destructor); surface->surface_actor = g_object_ref_sink (meta_surface_actor_wayland_new (surface)); wl_list_init (&surface->pending_frame_callback_list); g_signal_connect_object (surface->surface_actor, "notify::allocation", G_CALLBACK (surface_actor_allocation_notify), surface, 0); g_signal_connect_object (surface->surface_actor, "notify::position", G_CALLBACK (surface_actor_position_notify), surface, 0); g_signal_connect_object (surface->surface_actor, "notify::mapped", G_CALLBACK (surface_actor_mapped_notify), surface, 0); sync_drag_dest_funcs (surface); surface->outputs_to_destroy_notify_id = g_hash_table_new (NULL, NULL); surface->shortcut_inhibited_seats = g_hash_table_new (NULL, NULL); return surface; } gboolean meta_wayland_surface_begin_grab_op (MetaWaylandSurface *surface, MetaWaylandSeat *seat, MetaGrabOp grab_op, gfloat x, gfloat y) { MetaWindow *window = surface->window; 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->screen, 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); } static void unparent_actor (MetaWaylandSurface *surface) { ClutterActor *parent_actor; parent_actor = clutter_actor_get_parent (CLUTTER_ACTOR (surface->surface_actor)); clutter_actor_remove_child (parent_actor, CLUTTER_ACTOR (surface->surface_actor)); } static void wl_subsurface_destructor (struct wl_resource *resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (resource); meta_wayland_compositor_destroy_frame_callbacks (surface->compositor, surface); if (surface->sub.parent) { wl_list_remove (&surface->sub.parent_destroy_listener.link); surface->sub.parent->subsurfaces = g_list_remove (surface->sub.parent->subsurfaces, surface); unparent_actor (surface); surface->sub.parent = NULL; } g_clear_object (&surface->sub.pending); 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 (surface->sub.parent == sibling) return TRUE; if (surface->sub.parent == sibling->sub.parent) return TRUE; return FALSE; } 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; } static void queue_subsurface_placement (MetaWaylandSurface *surface, MetaWaylandSurface *sibling, MetaWaylandSubsurfacePlacement placement) { MetaWaylandSubsurfacePlacementOp *op = g_slice_new (MetaWaylandSubsurfacePlacementOp); op->placement = placement; op->sibling = sibling; op->sibling_destroy_listener.notify = subsurface_handle_pending_sibling_destroyed; wl_resource_add_destroy_listener (sibling->resource, &op->sibling_destroy_listener); surface->sub.pending_placement_ops = g_slist_append (surface->sub.pending_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)) apply_pending_state (surface, surface->sub.pending); } 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; unparent_actor (surface); } static void wl_subcompositor_get_subsurface (struct wl_client *client, struct wl_resource *resource, guint32 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); if (surface->wl_subsurface != NULL) { wl_resource_post_error (surface_resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "wl_subcompositor::get_subsurface already requested"); return; } if (!meta_wayland_surface_assign_role (surface, META_TYPE_WAYLAND_SURFACE_ROLE_SUBSURFACE, NULL)) { /* FIXME: There is no subcompositor "role" error yet, so lets just use something * similar until there is. */ wl_resource_post_error (resource, WL_SHELL_ERROR_ROLE, "wl_surface@%d already has a different role", wl_resource_get_id (surface->resource)); return; } 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.pending = g_object_new (META_TYPE_WAYLAND_PENDING_STATE, NULL); 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); parent->subsurfaces = g_list_append (parent->subsurfaces, surface); clutter_actor_add_child (CLUTTER_ACTOR (parent->surface_actor), CLUTTER_ACTOR (surface->surface_actor)); sync_reactive (surface); } 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, guint32 version, guint32 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_shell_init (MetaWaylandCompositor *compositor) { meta_wayland_xdg_shell_init (compositor); meta_wayland_wl_shell_init (compositor); meta_wayland_gtk_shell_init (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"); } void meta_wayland_surface_configure_notify (MetaWaylandSurface *surface, int new_x, int new_y, int new_width, int new_height, MetaWaylandSerial *sent_serial) { MetaWaylandSurfaceRoleShellSurface *shell_surface_role = META_WAYLAND_SURFACE_ROLE_SHELL_SURFACE (surface->role); g_signal_emit (surface, surface_signals[SURFACE_CONFIGURE], 0); meta_wayland_surface_role_shell_surface_configure (shell_surface_role, new_x, new_y, new_width, new_height, sent_serial); } void meta_wayland_surface_ping (MetaWaylandSurface *surface, guint32 serial) { MetaWaylandSurfaceRoleShellSurface *shell_surface_role = META_WAYLAND_SURFACE_ROLE_SHELL_SURFACE (surface->role); meta_wayland_surface_role_shell_surface_ping (shell_surface_role, serial); } void meta_wayland_surface_delete (MetaWaylandSurface *surface) { MetaWaylandSurfaceRoleShellSurface *shell_surface_role = META_WAYLAND_SURFACE_ROLE_SHELL_SURFACE (surface->role); meta_wayland_surface_role_shell_surface_close (shell_surface_role); } void meta_wayland_surface_window_managed (MetaWaylandSurface *surface, MetaWindow *window) { MetaWaylandSurfaceRoleShellSurface *shell_surface_role = META_WAYLAND_SURFACE_ROLE_SHELL_SURFACE (surface->role); meta_wayland_surface_role_shell_surface_managed (shell_surface_role, 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 toplevel->window; else return NULL; } void meta_wayland_surface_get_relative_coordinates (MetaWaylandSurface *surface, float abs_x, float abs_y, float *sx, float *sy) { /* Using clutter API to transform coordinates is only accurate right * after a clutter layout pass but this function is used e.g. to * deliver pointer motion events which can happen at any time. This * isn't a problem for wayland clients since they don't control * their position, but X clients do and we'd be sending outdated * coordinates if a client is moving a window in response to motion * events. */ if (surface->window && surface->window->client_type == META_WINDOW_CLIENT_TYPE_X11) { MetaRectangle window_rect; meta_window_get_buffer_rect (surface->window, &window_rect); *sx = abs_x - window_rect.x; *sy = abs_y - window_rect.y; } else { ClutterActor *actor = CLUTTER_ACTOR (meta_surface_actor_get_texture (surface->surface_actor)); clutter_actor_transform_stage_point (actor, abs_x, abs_y, sx, sy); *sx /= surface->scale; *sy /= surface->scale; } } void meta_wayland_surface_get_absolute_coordinates (MetaWaylandSurface *surface, float sx, float sy, float *x, float *y) { ClutterActor *actor = CLUTTER_ACTOR (meta_surface_actor_get_texture (surface->surface_actor)); ClutterVertex sv = { .x = sx * surface->scale, .y = sy * surface->scale, }; ClutterVertex 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 = g_object_new (META_TYPE_WAYLAND_PENDING_STATE, 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); } 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_commit (MetaWaylandSurfaceRole *surface_role, MetaWaylandPendingState *pending) { MetaWaylandSurfaceRoleClass *klass; klass = META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role); if (klass->pre_commit) klass->pre_commit (surface_role, pending); } static void meta_wayland_surface_role_commit (MetaWaylandSurfaceRole *surface_role, MetaWaylandPendingState *pending) { META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role)->commit (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; } MetaWaylandSurface * meta_wayland_surface_role_get_surface (MetaWaylandSurfaceRole *role) { MetaWaylandSurfaceRolePrivate *priv = meta_wayland_surface_role_get_instance_private (role); return priv->surface; } static void meta_wayland_surface_role_shell_surface_configure (MetaWaylandSurfaceRoleShellSurface *shell_surface_role, int new_x, int new_y, int new_width, int new_height, MetaWaylandSerial *sent_serial) { MetaWaylandSurfaceRoleShellSurfaceClass *shell_surface_role_class = META_WAYLAND_SURFACE_ROLE_SHELL_SURFACE_GET_CLASS (shell_surface_role); shell_surface_role_class->configure (shell_surface_role, new_x, new_y, new_width, new_height, sent_serial); } static void meta_wayland_surface_role_shell_surface_ping (MetaWaylandSurfaceRoleShellSurface *shell_surface_role, uint32_t serial) { MetaWaylandSurfaceRoleShellSurfaceClass *shell_surface_role_class = META_WAYLAND_SURFACE_ROLE_SHELL_SURFACE_GET_CLASS (shell_surface_role); shell_surface_role_class->ping (shell_surface_role, serial); } static void meta_wayland_surface_role_shell_surface_close (MetaWaylandSurfaceRoleShellSurface *shell_surface_role) { MetaWaylandSurfaceRoleShellSurfaceClass *shell_surface_role_class = META_WAYLAND_SURFACE_ROLE_SHELL_SURFACE_GET_CLASS (shell_surface_role); shell_surface_role_class->close (shell_surface_role); } static void meta_wayland_surface_role_shell_surface_managed (MetaWaylandSurfaceRoleShellSurface *shell_surface_role, MetaWindow *window) { MetaWaylandSurfaceRoleShellSurfaceClass *shell_surface_role_class = META_WAYLAND_SURFACE_ROLE_SHELL_SURFACE_GET_CLASS (shell_surface_role); shell_surface_role_class->managed (shell_surface_role, window); } void meta_wayland_surface_queue_pending_frame_callbacks (MetaWaylandSurface *surface) { wl_list_insert_list (&surface->compositor->frame_callbacks, &surface->pending_frame_callback_list); wl_list_init (&surface->pending_frame_callback_list); } static void default_role_assigned (MetaWaylandSurfaceRole *surface_role) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); meta_wayland_surface_queue_pending_frame_callbacks (surface); } static void actor_surface_assigned (MetaWaylandSurfaceRole *surface_role) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaSurfaceActorWayland *surface_actor = META_SURFACE_ACTOR_WAYLAND (surface->surface_actor); meta_surface_actor_wayland_add_frame_callbacks (surface_actor, &surface->pending_frame_callback_list); wl_list_init (&surface->pending_frame_callback_list); } static void actor_surface_commit (MetaWaylandSurfaceRole *surface_role, MetaWaylandPendingState *pending) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandSurface *toplevel_surface; queue_surface_actor_frame_callbacks (surface, pending); toplevel_surface = meta_wayland_surface_get_toplevel (surface); if (!toplevel_surface || !toplevel_surface->window) return; meta_surface_actor_wayland_sync_state ( META_SURFACE_ACTOR_WAYLAND (surface->surface_actor)); } static void meta_wayland_surface_role_actor_surface_init (MetaWaylandSurfaceRoleActorSurface *role) { } static void meta_wayland_surface_role_actor_surface_class_init (MetaWaylandSurfaceRoleActorSurfaceClass *klass) { MetaWaylandSurfaceRoleClass *surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); surface_role_class->assigned = actor_surface_assigned; surface_role_class->commit = actor_surface_commit; surface_role_class->is_on_logical_monitor = actor_surface_is_on_logical_monitor; } static void shell_surface_role_surface_commit (MetaWaylandSurfaceRole *surface_role, MetaWaylandPendingState *pending) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandSurfaceRoleClass *surface_role_class; MetaWindow *window; MetaWaylandBuffer *buffer; CoglTexture *texture; MetaSurfaceActorWayland *actor; double scale; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_surface_role_shell_surface_parent_class); surface_role_class->commit (surface_role, pending); buffer = surface->buffer_ref.buffer; if (!buffer) return; window = surface->window; if (!window) return; actor = META_SURFACE_ACTOR_WAYLAND (surface->surface_actor); scale = meta_surface_actor_wayland_get_scale (actor); texture = buffer->texture; window->buffer_rect.width = cogl_texture_get_width (texture) * scale; window->buffer_rect.height = cogl_texture_get_height (texture) * scale; } static void meta_wayland_surface_role_shell_surface_init (MetaWaylandSurfaceRoleShellSurface *role) { } static void meta_wayland_surface_role_shell_surface_class_init (MetaWaylandSurfaceRoleShellSurfaceClass *klass) { MetaWaylandSurfaceRoleClass *surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); surface_role_class->commit = shell_surface_role_surface_commit; } 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); surface_role_class->assigned = default_role_assigned; surface_role_class->commit = dnd_surface_commit; } static void meta_wayland_surface_role_subsurface_init (MetaWaylandSurfaceRoleSubsurface *role) { } static void meta_wayland_surface_role_subsurface_class_init (MetaWaylandSurfaceRoleSubsurfaceClass *klass) { MetaWaylandSurfaceRoleClass *surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); surface_role_class->commit = subsurface_role_commit; surface_role_class->get_toplevel = subsurface_role_get_toplevel; } cairo_region_t * meta_wayland_surface_calculate_input_region (MetaWaylandSurface *surface) { cairo_region_t *region; cairo_rectangle_int_t buffer_rect; CoglTexture *texture; if (!surface->buffer_ref.buffer) return NULL; texture = surface->buffer_ref.buffer->texture; buffer_rect = (cairo_rectangle_int_t) { .width = cogl_texture_get_width (texture) / surface->scale, .height = cogl_texture_get_height (texture) / surface->scale, }; 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); } ukwm/src/wayland/meta-wayland-inhibit-shortcuts.c0000664000175000017500000001632513233511035021123 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Olivier Fourdan */ #include "config.h" #include #include "keyboard-shortcuts-inhibit-unstable-v1-server-protocol.h" #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" 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_signal_handler_disconnect (shortcut_inhibit->surface, shortcut_inhibit->surface_destroyed_handler); g_signal_handler_disconnect (shortcut_inhibit->surface, shortcut_inhibit->inhibit_shortcut_handler); g_signal_handler_disconnect (shortcut_inhibit->surface, shortcut_inhibit->restore_shortcut_handler); 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) { MetaWaylandKeyboard *keyboard = shortcut_inhibit->seat->keyboard; /* Send active event only if the surface has keyboard focus */ if (keyboard->focus_surface == surface) zwp_keyboard_shortcuts_inhibitor_v1_send_active (shortcut_inhibit->resource); } static void shortcuts_restored_cb (MetaWaylandSurface *surface, gpointer user_data) { MetaWaylandKeyboardShotscutsInhibit *shortcut_inhibit = user_data; 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); 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); } ukwm/src/wayland/meta-xwayland-selection.c0000664000175000017500000016515413227625657017646 0ustar fengfeng/* * Copyright © 2012 Intel Corporation * Copyright © 2017 Tianjin KYLIN Information Technology Co., 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 loosely based on xwayland/selection.c from Weston */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "meta-xwayland.h" #include "meta-xwayland-private.h" #include "meta-xwayland-selection-private.h" #include "meta-wayland-data-device.h" #define INCR_CHUNK_SIZE (128 * 1024) #define XDND_VERSION 5 typedef struct { MetaXWaylandSelection *selection_data; GInputStream *stream; GCancellable *cancellable; MetaWindow *window; XSelectionRequestEvent request_event; guchar buffer[INCR_CHUNK_SIZE]; gsize buffer_len; guint incr : 1; } WaylandSelectionData; typedef struct { MetaXWaylandSelection *selection_data; GOutputStream *stream; GCancellable *cancellable; gchar *mime_type; guint incr : 1; } X11SelectionData; typedef struct { Atom selection_atom; Window window; Window owner; Time timestamp; Time client_message_timestamp; MetaWaylandDataSource *source; /* owned by MetaWaylandDataDevice */ WaylandSelectionData *wayland_selection; X11SelectionData *x11_selection; struct wl_listener ownership_listener; } MetaSelectionBridge; typedef struct { MetaSelectionBridge selection; MetaWaylandSurface *focus_surface; Window dnd_window; /* Ukwm-internal window, acts as peer on wayland drop sites */ Window dnd_dest; /* X11 drag dest window */ guint32 last_motion_time; } MetaDndBridge; struct _MetaWaylandDataSourceXWayland { MetaWaylandDataSource parent; MetaSelectionBridge *selection; guint has_utf8_string_atom : 1; }; struct _MetaXWaylandSelection { MetaSelectionBridge clipboard; MetaSelectionBridge primary; MetaDndBridge dnd; }; 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 (MetaXWaylandSelection *selection_data, Window dest) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaSelectionBridge *selection = &selection_data->dnd.selection; Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); MetaWaylandDataSource *data_source; XEvent xev = { 0 }; gchar **p; struct wl_array *source_mime_types; 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] = selection->window; 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. */ Atom *atomlist; 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, selection->window, xdnd_atoms[ATOM_DND_TYPE_LIST], XA_ATOM, 32, PropModeReplace, (guchar *) atomlist, i); } XSendEvent (xdisplay, dest, False, NoEventMask, &xev); } static void xdnd_send_leave (MetaXWaylandSelection *selection_data, Window dest) { MetaSelectionBridge *selection = &selection_data->dnd.selection; Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); 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] = selection->window; XSendEvent (xdisplay, dest, False, NoEventMask, &xev); } static void xdnd_send_position (MetaXWaylandSelection *selection_data, Window dest, uint32_t time, int x, int y) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaSelectionBridge *selection = &selection_data->dnd.selection; MetaWaylandDataSource *source = compositor->seat->data_device.dnd_data_source; Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); uint32_t action = 0, user_action, actions; XEvent xev = { 0 }; user_action = meta_wayland_data_source_get_user_action (source); actions = meta_wayland_data_source_get_actions (source); 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] = selection->window; 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); XSendEvent (xdisplay, dest, False, NoEventMask, &xev); } static void xdnd_send_drop (MetaXWaylandSelection *selection_data, Window dest, uint32_t time) { MetaSelectionBridge *selection = &selection_data->dnd.selection; Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); 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] = selection->window; xev.xclient.data.l[2] = time; XSendEvent (xdisplay, dest, False, NoEventMask, &xev); } static void xdnd_send_finished (MetaXWaylandSelection *selection_data, Window dest, gboolean accepted) { MetaDndBridge *selection = &selection_data->dnd; Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); MetaWaylandDataSource *source = selection_data->dnd.selection.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] = selection->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); } XSendEvent (xdisplay, dest, False, NoEventMask, &xev); } static void xdnd_send_status (MetaXWaylandSelection *selection_data, Window dest, uint32_t action) { MetaDndBridge *selection = &selection_data->dnd; 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] = selection->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 */ XSendEvent (xdisplay, dest, False, NoEventMask, &xev); } static void meta_xwayland_init_dnd (MetaXWaylandManager *manager) { Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); MetaDndBridge *dnd = &manager->selection_data->dnd; XSetWindowAttributes attributes; guint32 i, version = XDND_VERSION; 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); } static void meta_xwayland_shutdown_dnd (MetaXWaylandManager *manager) { MetaDndBridge *dnd = &manager->selection_data->dnd; XDestroyWindow (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), dnd->dnd_window); dnd->dnd_window = None; } static void meta_xwayland_end_dnd_grab (MetaWaylandDataDevice *data_device) { Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaXWaylandManager *manager = &compositor->xwayland_manager; MetaDndBridge *dnd = &manager->selection_data->dnd; meta_wayland_data_device_end_drag (data_device); XMoveResizeWindow (xdisplay, dnd->dnd_window, -1, -1, 1, 1); XUnmapWindow (xdisplay, dnd->dnd_window); } /* X11/Wayland data bridges */ static MetaSelectionBridge * atom_to_selection_bridge (MetaWaylandCompositor *compositor, Atom selection_atom) { MetaXWaylandSelection *selection_data = compositor->xwayland_manager.selection_data; if (selection_atom == selection_data->clipboard.selection_atom) return &selection_data->clipboard; else if (selection_atom == selection_data->primary.selection_atom) return &selection_data->primary; else if (selection_atom == selection_data->dnd.selection.selection_atom) return &selection_data->dnd.selection; else return NULL; } static X11SelectionData * x11_selection_data_new (MetaXWaylandSelection *selection_data, int fd, const char *mime_type) { X11SelectionData *data; data = g_slice_new0 (X11SelectionData); data->selection_data = selection_data; data->stream = g_unix_output_stream_new (fd, TRUE); data->cancellable = g_cancellable_new (); data->mime_type = g_strdup (mime_type); return data; } static void x11_selection_data_free (X11SelectionData *data) { g_cancellable_cancel (data->cancellable); g_object_unref (data->cancellable); g_object_unref (data->stream); g_free (data->mime_type); g_slice_free (X11SelectionData, data); } static void x11_selection_data_send_finished (MetaSelectionBridge *selection, gboolean success) { uint32_t action = meta_wayland_data_source_get_current_action (selection->source); if (!selection->x11_selection) return; if (success && 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, selection->selection_atom, gdk_x11_get_xatom_by_name ("DELETE"), gdk_x11_get_xatom_by_name ("_META_SELECTION"), selection->window, CurrentTime); } xdnd_send_finished (selection->x11_selection->selection_data, selection->owner, success); } static void x11_selection_data_finish (MetaSelectionBridge *selection, gboolean success) { if (!selection->x11_selection) return; if (selection == &selection->x11_selection->selection_data->dnd.selection) x11_selection_data_send_finished (selection, success); g_clear_pointer (&selection->x11_selection, (GDestroyNotify) x11_selection_data_free); } static void x11_selection_data_close (X11SelectionData *data) { g_output_stream_close (data->stream, data->cancellable, NULL); } static void x11_data_write_cb (GObject *object, GAsyncResult *res, gpointer user_data) { MetaSelectionBridge *selection = user_data; X11SelectionData *data = selection->x11_selection; GError *error = NULL; gboolean success = TRUE; g_output_stream_write_finish (G_OUTPUT_STREAM (object), res, &error); if (error) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free (error); return; } g_warning ("Error writing from X11 selection: %s\n", error->message); g_error_free (error); success = FALSE; } if (success && data->incr) { Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); XDeleteProperty (xdisplay, selection->window, gdk_x11_get_xatom_by_name ("_META_SELECTION")); } else { x11_selection_data_close (selection->x11_selection); x11_selection_data_finish (selection, success); } } static void x11_selection_data_write (MetaSelectionBridge *selection, guchar *buffer, gulong len) { X11SelectionData *data = selection->x11_selection; g_output_stream_write_async (data->stream, buffer, len, G_PRIORITY_DEFAULT, data->cancellable, x11_data_write_cb, selection); } static MetaWaylandDataSource * data_device_get_active_source_for_atom (MetaWaylandDataDevice *data_device, Atom selection_atom) { if (selection_atom == gdk_x11_get_xatom_by_name ("CLIPBOARD")) return data_device->selection_data_source; else if (selection_atom == gdk_x11_get_xatom_by_name ("PRIMARY")) return data_device->primary_data_source; else if (selection_atom == xdnd_atoms[ATOM_DND_SELECTION]) return data_device->dnd_data_source; else return NULL; } static WaylandSelectionData * wayland_selection_data_new (XSelectionRequestEvent *request_event, MetaWaylandCompositor *compositor) { MetaDisplay *display = meta_get_display (); MetaScreen *screen = display->screen; MetaWaylandDataDevice *data_device; MetaWaylandDataSource *wayland_source; MetaSelectionBridge *selection; WaylandSelectionData *data; const gchar *mime_type; GError *error = NULL; int p[2]; selection = atom_to_selection_bridge (compositor, request_event->selection); if (!selection) return NULL; if (!g_unix_open_pipe (p, FD_CLOEXEC, &error)) { g_critical ("Failed to open pipe: %s\n", error->message); g_error_free (error); return NULL; } data_device = &compositor->seat->data_device; mime_type = gdk_x11_get_xatom_name (request_event->target); if (!g_unix_set_fd_nonblocking (p[0], TRUE, &error) || !g_unix_set_fd_nonblocking (p[1], TRUE, &error)) { if (error) { g_critical ("Failed to make fds non-blocking: %s\n", error->message); g_error_free (error); } close (p[0]); close (p[1]); return NULL; } wayland_source = data_device_get_active_source_for_atom (data_device, selection->selection_atom), meta_wayland_data_source_send (wayland_source, mime_type, p[1]); data = g_slice_new0 (WaylandSelectionData); data->request_event = *request_event; data->cancellable = g_cancellable_new (); data->stream = g_unix_input_stream_new (p[0], TRUE); data->window = meta_display_lookup_x_window (meta_get_display (), data->request_event.requestor); /* Do *not* change the event mask on the root window, bugger! */ if (!data->window && data->request_event.requestor != screen->xroot) { /* Not a managed window, set the PropertyChangeMask * for INCR deletion notifications. */ XSelectInput (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), data->request_event.requestor, PropertyChangeMask); } return data; } static void reply_selection_request (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 wayland_selection_data_free (WaylandSelectionData *data) { MetaDisplay *display = meta_get_display (); MetaScreen *screen = display->screen; /* Do *not* change the event mask on the root window, bugger! */ if (!data->window && data->request_event.requestor != screen->xroot) { meta_error_trap_push (display); XSelectInput (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), data->request_event.requestor, NoEventMask); meta_error_trap_pop (display); } g_cancellable_cancel (data->cancellable); g_object_unref (data->cancellable); g_object_unref (data->stream); g_slice_free (WaylandSelectionData, data); } static void wayland_selection_update_x11_property (WaylandSelectionData *data) { Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); XChangeProperty (xdisplay, data->request_event.requestor, data->request_event.property, data->request_event.target, 8, PropModeReplace, data->buffer, data->buffer_len); data->buffer_len = 0; } static void wayland_data_read_cb (GObject *object, GAsyncResult *res, gpointer user_data) { MetaSelectionBridge *selection = user_data; WaylandSelectionData *data = selection->wayland_selection; GError *error = NULL; gsize bytes_read; bytes_read = g_input_stream_read_finish (G_INPUT_STREAM (object), res, &error); if (error) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free (error); return; } g_warning ("Error transferring wayland clipboard to X11: %s\n", error->message); g_error_free (error); if (data && data->stream == G_INPUT_STREAM (object)) { reply_selection_request (&data->request_event, FALSE); g_clear_pointer (&selection->wayland_selection, (GDestroyNotify) wayland_selection_data_free); } return; } data->buffer_len = bytes_read; if (bytes_read == INCR_CHUNK_SIZE) { if (!data->incr) { Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); guint32 incr_chunk_size = INCR_CHUNK_SIZE; /* Not yet in incr */ data->incr = TRUE; XChangeProperty (xdisplay, data->request_event.requestor, data->request_event.property, gdk_x11_get_xatom_by_name ("INCR"), 32, PropModeReplace, (guchar *) &incr_chunk_size, 1); reply_selection_request (&data->request_event, TRUE); } else wayland_selection_update_x11_property (data); } else { if (!data->incr) { /* Non-incr transfer finished */ wayland_selection_update_x11_property (data); reply_selection_request (&data->request_event, TRUE); } else if (data->incr) { /* Incr transfer complete, setting a new property */ wayland_selection_update_x11_property (data); if (bytes_read > 0) return; } g_clear_pointer (&selection->wayland_selection, (GDestroyNotify) wayland_selection_data_free); } } static void wayland_selection_data_read (MetaSelectionBridge *selection) { WaylandSelectionData *data = selection->wayland_selection; g_input_stream_read_async (data->stream, data->buffer, INCR_CHUNK_SIZE, G_PRIORITY_DEFAULT, data->cancellable, wayland_data_read_cb, selection); } static void meta_xwayland_selection_get_incr_chunk (MetaWaylandCompositor *compositor, MetaSelectionBridge *selection) { Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); gulong nitems_ret, bytes_after_ret; guchar *prop_ret; int format_ret; Atom type_ret; XGetWindowProperty (xdisplay, selection->window, gdk_x11_get_xatom_by_name ("_META_SELECTION"), 0, /* offset */ 0x1fffffff, /* length */ False, /* delete */ AnyPropertyType, &type_ret, &format_ret, &nitems_ret, &bytes_after_ret, &prop_ret); if (nitems_ret > 0) { x11_selection_data_write (selection, prop_ret, nitems_ret); } else { /* Transfer has completed */ x11_selection_data_close (selection->x11_selection); x11_selection_data_finish (selection, TRUE); } XFree (prop_ret); } static void meta_x11_source_send (MetaWaylandDataSource *source, const gchar *mime_type, gint fd) { MetaWaylandDataSourceXWayland *source_xwayland = META_WAYLAND_DATA_SOURCE_XWAYLAND (source); MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); MetaSelectionBridge *selection = source_xwayland->selection; Atom type_atom; if (source_xwayland->has_utf8_string_atom && strcmp (mime_type, "text/plain;charset=utf-8") == 0) type_atom = gdk_x11_get_xatom_by_name ("UTF8_STRING"); else type_atom = gdk_x11_get_xatom_by_name (mime_type); /* Ensure we close previous transactions */ x11_selection_data_finish (selection, FALSE); /* Takes ownership of fd */ selection->x11_selection = x11_selection_data_new (compositor->xwayland_manager.selection_data, fd, gdk_x11_get_xatom_name (type_atom)); XConvertSelection (xdisplay, selection->selection_atom, type_atom, gdk_x11_get_xatom_by_name ("_META_SELECTION"), selection->window, selection->client_message_timestamp); XFlush (xdisplay); } static void meta_x11_source_target (MetaWaylandDataSource *source, const gchar *mime_type) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaWaylandDataSourceXWayland *source_xwayland = META_WAYLAND_DATA_SOURCE_XWAYLAND (source); MetaSelectionBridge *selection = source_xwayland->selection; uint32_t action = 0; if (selection->selection_atom == xdnd_atoms[ATOM_DND_SELECTION]) { if (mime_type) action = meta_wayland_data_source_get_current_action (source); xdnd_send_status (compositor->xwayland_manager.selection_data, selection->owner, action); } } static void meta_x11_source_cancel (MetaWaylandDataSource *source) { MetaWaylandDataSourceXWayland *source_xwayland = META_WAYLAND_DATA_SOURCE_XWAYLAND (source); MetaSelectionBridge *selection = source_xwayland->selection; x11_selection_data_send_finished (selection, FALSE); g_clear_pointer (&selection->x11_selection, (GDestroyNotify) x11_selection_data_free); } static void meta_x11_source_action (MetaWaylandDataSource *source, uint32_t action) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaWaylandDataSourceXWayland *source_xwayland = META_WAYLAND_DATA_SOURCE_XWAYLAND (source); MetaSelectionBridge *selection = source_xwayland->selection; if (selection->selection_atom == xdnd_atoms[ATOM_DND_SELECTION]) { if (!meta_wayland_data_source_has_target (source)) action = 0; xdnd_send_status (compositor->xwayland_manager.selection_data, selection->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); MetaSelectionBridge *selection = source_xwayland->selection; if (selection->x11_selection) x11_selection_data_send_finished (selection, 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 (MetaSelectionBridge *selection) { MetaWaylandDataSourceXWayland *source_xwayland; source_xwayland = g_object_new (META_TYPE_WAYLAND_DATA_SOURCE_XWAYLAND, NULL); source_xwayland->selection = selection; 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 (); compositor->xwayland_manager.selection_data->dnd.dnd_dest = surface->window->xwindow; xdnd_send_enter (compositor->xwayland_manager.selection_data, compositor->xwayland_manager.selection_data->dnd.dnd_dest); } static void meta_x11_drag_dest_focus_out (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); xdnd_send_leave (compositor->xwayland_manager.selection_data, compositor->xwayland_manager.selection_data->dnd.dnd_dest); compositor->xwayland_manager.selection_data->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 (); guint32 time; gfloat x, y; time = clutter_event_get_time (event); clutter_event_get_coords (event, &x, &y); xdnd_send_position (compositor->xwayland_manager.selection_data, compositor->xwayland_manager.selection_data->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 (); xdnd_send_drop (compositor->xwayland_manager.selection_data, compositor->xwayland_manager.selection_data->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 (); MetaWaylandSeat *seat = compositor->seat; ClutterPoint pos; clutter_input_device_get_coords (seat->pointer->device, NULL, &pos); xdnd_send_position (compositor->xwayland_manager.selection_data, compositor->xwayland_manager.selection_data->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 */ True, /* 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 void meta_xwayland_selection_get_x11_targets (MetaWaylandCompositor *compositor, MetaSelectionBridge *selection) { MetaWaylandDataSource *data_source; data_source = meta_wayland_data_source_xwayland_new (selection); if (meta_xwayland_data_source_fetch_mimetype_list (data_source, selection->window, gdk_x11_get_xatom_by_name ("_META_SELECTION"))) { g_clear_object (&selection->source); selection->source = data_source; if (selection->selection_atom == gdk_x11_get_xatom_by_name ("CLIPBOARD")) { meta_wayland_data_device_set_selection (&compositor->seat->data_device, data_source, wl_display_next_serial (compositor->wayland_display)); } else if (selection->selection_atom == gdk_x11_get_xatom_by_name ("PRIMARY")) { meta_wayland_data_device_set_primary (&compositor->seat->data_device, data_source, wl_display_next_serial (compositor->wayland_display)); } } else g_object_unref (data_source); } static void meta_xwayland_selection_get_x11_data (MetaWaylandCompositor *compositor, MetaSelectionBridge *selection) { Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); gulong nitems_ret, bytes_after_ret; guchar *prop_ret; int format_ret; Atom type_ret; if (!selection->x11_selection) return; XGetWindowProperty (xdisplay, selection->window, gdk_x11_get_xatom_by_name ("_META_SELECTION"), 0, /* offset */ 0x1fffffff, /* length */ True, /* delete */ AnyPropertyType, &type_ret, &format_ret, &nitems_ret, &bytes_after_ret, &prop_ret); selection->x11_selection->incr = (type_ret == gdk_x11_get_xatom_by_name ("INCR")); if (selection->x11_selection->incr) return; if (type_ret == gdk_x11_get_xatom_by_name (selection->x11_selection->mime_type)) x11_selection_data_write (selection, prop_ret, nitems_ret); XFree (prop_ret); } static gboolean meta_xwayland_selection_handle_selection_notify (MetaWaylandCompositor *compositor, XEvent *xevent) { XSelectionEvent *event = (XSelectionEvent *) xevent; MetaSelectionBridge *selection; selection = atom_to_selection_bridge (compositor, event->selection); if (!selection) return FALSE; if (selection->window != event->requestor) return FALSE; /* convert selection failed */ if (event->property == None) { g_clear_pointer (&selection->x11_selection, (GDestroyNotify) x11_selection_data_free); return FALSE; } if (event->target == gdk_x11_get_xatom_by_name ("TARGETS")) meta_xwayland_selection_get_x11_targets (compositor, selection); else meta_xwayland_selection_get_x11_data (compositor, selection); return TRUE; } static void meta_xwayland_selection_send_targets (MetaWaylandCompositor *compositor, const MetaWaylandDataSource *data_source, Window requestor, Atom property) { Atom *targets; gchar **p; int i = 0; struct wl_array *source_mime_types; if (!data_source) return; source_mime_types = meta_wayland_data_source_get_mime_types (data_source); if (source_mime_types->size == 0) return; /* Make extra room for TIMESTAMP/TARGETS */ targets = g_new (Atom, source_mime_types->size + 2); wl_array_for_each (p, source_mime_types) { targets[i++] = gdk_x11_get_xatom_by_name (*p); } targets[i++] = gdk_x11_get_xatom_by_name ("TIMESTAMP"); targets[i++] = gdk_x11_get_xatom_by_name ("TARGETS"); XChangeProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), requestor, property, XA_ATOM, 32, PropModeReplace, (guchar *) targets, i); g_free (targets); } static void meta_xwayland_selection_send_timestamp (MetaWaylandCompositor *compositor, Window requestor, Atom property, Time timestamp) { Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); XChangeProperty (xdisplay, requestor, property, XA_INTEGER, 32, PropModeReplace, (guchar *) ×tamp, 1); } static void meta_xwayland_selection_send_incr_chunk (MetaWaylandCompositor *compositor, MetaSelectionBridge *selection) { if (!selection->wayland_selection) return; if (selection->wayland_selection->buffer_len > 0) wayland_selection_update_x11_property (selection->wayland_selection); else wayland_selection_data_read (selection); } static gboolean handle_incr_chunk (MetaWaylandCompositor *compositor, MetaSelectionBridge *selection, XPropertyEvent *event) { if (selection->x11_selection && selection->x11_selection->incr && event->window == selection->owner && event->state == PropertyNewValue && event->atom == gdk_x11_get_xatom_by_name ("_META_SELECTION")) { /* X11 to Wayland */ meta_xwayland_selection_get_incr_chunk (compositor, selection); return TRUE; } else if (selection->wayland_selection && selection->wayland_selection->incr && event->window == selection->window && event->state == PropertyDelete && event->atom == selection->wayland_selection->request_event.property) { /* Wayland to X11 */ meta_xwayland_selection_send_incr_chunk (compositor, selection); return TRUE; } return FALSE; } static gboolean meta_xwayland_selection_handle_property_notify (MetaWaylandCompositor *compositor, XEvent *xevent) { MetaXWaylandSelection *selection_data = compositor->xwayland_manager.selection_data; XPropertyEvent *event = (XPropertyEvent *) xevent; return handle_incr_chunk (compositor, &selection_data->clipboard, event); } static gboolean meta_xwayland_selection_handle_selection_request (MetaWaylandCompositor *compositor, XEvent *xevent) { XSelectionRequestEvent *event = (XSelectionRequestEvent *) xevent; MetaWaylandDataSource *data_source; MetaSelectionBridge *selection; selection = atom_to_selection_bridge (compositor, event->selection); if (!selection) return FALSE; if (selection->window != event->owner) return FALSE; /* We must fetch from the currently active source, not the Xwayland one */ data_source = data_device_get_active_source_for_atom (&compositor->seat->data_device, selection->selection_atom); if (!data_source) return FALSE; g_clear_pointer (&selection->wayland_selection, (GDestroyNotify) wayland_selection_data_free); if (event->target == gdk_x11_get_xatom_by_name ("TARGETS")) { meta_xwayland_selection_send_targets (compositor, data_source, event->requestor, event->property); reply_selection_request (event, TRUE); } else if (event->target == gdk_x11_get_xatom_by_name ("TIMESTAMP")) { meta_xwayland_selection_send_timestamp (compositor, event->requestor, event->property, selection->timestamp); reply_selection_request (event, TRUE); } else if (data_source && event->target == gdk_x11_get_xatom_by_name ("DELETE")) { reply_selection_request (event, TRUE); } else { if (data_source && meta_wayland_data_source_has_mime_type (data_source, gdk_x11_get_xatom_name (event->target))) { selection->wayland_selection = wayland_selection_data_new (event, compositor); if (selection->wayland_selection) wayland_selection_data_read (selection); } if (!selection->wayland_selection) reply_selection_request (event, FALSE); } return TRUE; } static MetaWaylandSurface * pick_drop_surface (MetaWaylandCompositor *compositor, const ClutterEvent *event) { MetaDisplay *display = meta_get_display (); MetaWindow *focus_window = NULL; ClutterPoint pos; clutter_event_get_coords (event, &pos.x, &pos.y); focus_window = meta_stack_get_default_focus_window_at_point (display->screen->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 ()); MetaDndBridge *dnd = &compositor->xwayland_manager.selection_data->dnd; MetaWaylandSurface *focus = NULL; focus = pick_drop_surface (compositor, event); if (dnd->focus_surface == focus) return; dnd->focus_surface = focus; if (focus && 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 (); MetaDndBridge *dnd = &compositor->xwayland_manager.selection_data->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); } static const MetaWaylandPointerGrabInterface drag_xgrab_interface = { drag_xgrab_focus, drag_xgrab_motion, drag_xgrab_button, }; static gboolean meta_xwayland_selection_handle_client_message (MetaWaylandCompositor *compositor, XEvent *xevent) { XClientMessageEvent *event = (XClientMessageEvent *) xevent; MetaDndBridge *dnd = &compositor->xwayland_manager.selection_data->dnd; MetaWaylandSeat *seat = compositor->seat; /* Source side messages */ if (event->window == dnd->selection.window) { 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->selection.source && compositor->seat->data_device.current_grab && (Window) event->data.l[0] == dnd->selection.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->selection.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->selection.source, mimetype); } } } else { /* Fetch mimetypes from type list */ meta_xwayland_data_source_fetch_mimetype_list (dnd->selection.source, event->data.l[0], xdnd_atoms[ATOM_DND_TYPE_LIST]); } 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; ClutterPoint pos; uint32_t action = 0; dnd->selection.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_actions (dnd->selection.source, action); meta_wayland_surface_drag_dest_motion (drag_focus, motion); xdnd_send_status (compositor->xwayland_manager.selection_data, (Window) event->data.l[0], meta_wayland_data_source_get_current_action (dnd->selection.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->selection.client_message_timestamp = event->data.l[2]; meta_wayland_surface_drag_dest_drop (drag_focus); meta_xwayland_end_dnd_grab (&seat->data_device); return TRUE; } } return FALSE; } static gboolean meta_xwayland_selection_handle_xfixes_selection_notify (MetaWaylandCompositor *compositor, XEvent *xevent) { XFixesSelectionNotifyEvent *event = (XFixesSelectionNotifyEvent *) xevent; Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); MetaSelectionBridge *selection; selection = atom_to_selection_bridge (compositor, event->selection); if (!selection) return FALSE; if (selection->selection_atom == gdk_x11_get_xatom_by_name ("CLIPBOARD") || selection->selection_atom == gdk_x11_get_xatom_by_name ("PRIMARY")) { if (event->owner == None) { if (selection->source && selection->owner != selection->window) { /* An X client went away, clear the selection */ g_clear_object (&selection->source); } selection->owner = None; } else { selection->owner = event->owner; if (selection->owner == selection->window) { /* This our own selection window */ selection->timestamp = event->timestamp; return TRUE; } g_clear_pointer (&selection->x11_selection, (GDestroyNotify) x11_selection_data_free); XConvertSelection (xdisplay, event->selection, gdk_x11_get_xatom_by_name ("TARGETS"), gdk_x11_get_xatom_by_name ("_META_SELECTION"), selection->window, CurrentTime); XFlush (xdisplay); } } else if (selection->selection_atom == xdnd_atoms[ATOM_DND_SELECTION]) { MetaWaylandDataDevice *data_device = &compositor->seat->data_device; MetaWaylandSurface *focus; selection->owner = event->owner; focus = compositor->seat->pointer->focus_surface; if (event->owner != None && event->owner != selection->window && focus && meta_xwayland_is_xwayland_surface (focus)) { selection->client_message_timestamp = CurrentTime; selection->source = meta_wayland_data_source_xwayland_new (selection); meta_wayland_data_device_set_dnd_source (&compositor->seat->data_device, selection->source); meta_wayland_data_device_start_drag (data_device, wl_resource_get_client (focus->resource), &drag_xgrab_interface, focus, selection->source, NULL); } else if (event->owner == None) { meta_xwayland_end_dnd_grab (data_device); } } return TRUE; } gboolean meta_xwayland_selection_handle_event (XEvent *xevent) { MetaWaylandCompositor *compositor; compositor = meta_wayland_compositor_get_default (); if (!compositor->xwayland_manager.selection_data) return FALSE; switch (xevent->type) { case SelectionNotify: return meta_xwayland_selection_handle_selection_notify (compositor, xevent); case PropertyNotify: return meta_xwayland_selection_handle_property_notify (compositor, xevent); case SelectionRequest: return meta_xwayland_selection_handle_selection_request (compositor, xevent); case ClientMessage: return meta_xwayland_selection_handle_client_message (compositor, xevent); default: { MetaDisplay *display = meta_get_display (); if (xevent->type - display->xfixes_event_base == XFixesSelectionNotify) return meta_xwayland_selection_handle_xfixes_selection_notify (compositor, xevent); return FALSE; } } } static void meta_selection_bridge_ownership_notify (struct wl_listener *listener, void *data) { Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); MetaSelectionBridge *selection = wl_container_of (listener, selection, ownership_listener); MetaWaylandDataSource *owner = data; if (!owner && selection->window == selection->owner) { XSetSelectionOwner (xdisplay, selection->selection_atom, None, selection->timestamp); } else if (owner && selection->source != owner) { XSetSelectionOwner (xdisplay, selection->selection_atom, selection->window, CurrentTime); } } static void init_selection_bridge (MetaSelectionBridge *selection, Atom selection_atom, struct wl_signal *signal) { Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); XSetWindowAttributes attributes; guint mask; attributes.event_mask = PropertyChangeMask; selection->ownership_listener.notify = meta_selection_bridge_ownership_notify; wl_signal_add (signal, &selection->ownership_listener); selection->selection_atom = selection_atom; selection->window = XCreateWindow (xdisplay, gdk_x11_window_get_xid (gdk_get_default_root_window ()), -1, -1, 1, 1, /* position */ 0, /* border width */ 0, /* depth */ InputOnly, /* class */ CopyFromParent, /* visual */ CWEventMask, &attributes); mask = XFixesSetSelectionOwnerNotifyMask | XFixesSelectionWindowDestroyNotifyMask | XFixesSelectionClientCloseNotifyMask; XFixesSelectSelectionInput (xdisplay, selection->window, selection_atom, mask); } static void shutdown_selection_bridge (MetaSelectionBridge *selection) { wl_list_remove (&selection->ownership_listener.link); XDestroyWindow (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), selection->window); g_clear_pointer (&selection->wayland_selection, (GDestroyNotify) wayland_selection_data_free); g_clear_pointer (&selection->x11_selection, (GDestroyNotify) x11_selection_data_free); } void meta_xwayland_init_selection (void) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaXWaylandManager *manager = &compositor->xwayland_manager; g_assert (manager->selection_data == NULL); manager->selection_data = g_slice_new0 (MetaXWaylandSelection); meta_xwayland_init_dnd (manager); init_selection_bridge (&manager->selection_data->clipboard, gdk_x11_get_xatom_by_name ("CLIPBOARD"), &compositor->seat->data_device.selection_ownership_signal); init_selection_bridge (&manager->selection_data->primary, gdk_x11_get_xatom_by_name ("PRIMARY"), &compositor->seat->data_device.primary_ownership_signal); init_selection_bridge (&manager->selection_data->dnd.selection, xdnd_atoms[ATOM_DND_SELECTION], &compositor->seat->data_device.dnd_ownership_signal); } void meta_xwayland_shutdown_selection (void) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaXWaylandManager *manager = &compositor->xwayland_manager; MetaXWaylandSelection *selection = manager->selection_data; g_assert (selection != NULL); g_clear_object (&selection->clipboard.source); meta_xwayland_shutdown_dnd (manager); shutdown_selection_bridge (&selection->clipboard); shutdown_selection_bridge (&selection->primary); shutdown_selection_bridge (&selection->dnd.selection); g_slice_free (MetaXWaylandSelection, selection); manager->selection_data = NULL; } ukwm/src/wayland/meta-wayland-xdg-shell.h0000664000175000017500000000367713233511035017343 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef META_WAYLAND_XDG_SHELL_H #define META_WAYLAND_XDG_SHELL_H #include "wayland/meta-wayland-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, MetaWaylandSurfaceRoleShellSurface); struct _MetaWaylandXdgSurfaceClass { MetaWaylandSurfaceRoleShellSurfaceClass parent_class; void (*shell_client_destroyed) (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 */ ukwm/src/wayland/meta-wayland-touch.c0000664000175000017500000004332713233511035016565 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author: Carlos Garnacho */ #include "config.h" #include #include #include #include "meta-surface-actor-wayland.h" #include "meta-wayland-private.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-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; }; 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 = clutter_evdev_event_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); } void meta_wayland_touch_update (MetaWaylandTouch *touch, const ClutterEvent *event) { 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 || 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; } static void handle_touch_begin (MetaWaylandTouch *touch, const ClutterEvent *event) { 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)); } } static void handle_touch_update (MetaWaylandTouch *touch, const ClutterEvent *event) { 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)); } } static void handle_touch_end (MetaWaylandTouch *touch, const ClutterEvent *event) { 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); } 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) { ClutterEventSequence *sequence; gint32 slot; sequence = clutter_event_get_event_sequence (event); slot = clutter_evdev_event_sequence_get_slot (sequence); touch->frame_slots &= ~(1 << slot); if (touch->frame_slots == 0) 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, }; static void touch_info_free (MetaWaylandTouchInfo *touch_info) { touch_surface_decrement_touch (touch_info->touch_surface); g_free (touch_info); } 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) { ClutterDeviceManager *manager; 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); wl_list_init (&touch->resource_list); manager = clutter_device_manager_get_default (); touch->device = clutter_device_manager_get_core_device (manager, CLUTTER_TOUCHSCREEN_DEVICE); #ifdef HAVE_NATIVE_BACKEND MetaBackend *backend = meta_get_backend (); if (META_IS_BACKEND_NATIVE (backend)) clutter_evdev_add_filter (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)) clutter_evdev_remove_filter (evdev_filter_func, touch); #endif meta_wayland_touch_cancel (touch); g_clear_pointer (&touch->touch_surfaces, (GDestroyNotify) g_hash_table_unref); g_clear_pointer (&touch->touches, (GDestroyNotify) 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) { } ukwm/src/wayland/meta-wayland-dma-buf.c0000664000175000017500000004720113233511035016751 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl * Daniel Stone */ #include "config.h" #include "wayland/meta-wayland-dma-buf.h" #include "cogl/cogl.h" #include "cogl/cogl-egl.h" #include "backends/meta-backend-private.h" #include "backends/meta-egl.h" #include "backends/meta-egl-ext.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 #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]; int offsets[META_WAYLAND_DMA_BUF_MAX_FDS]; unsigned int strides[META_WAYLAND_DMA_BUF_MAX_FDS]; }; G_DEFINE_TYPE (MetaWaylandDmaBufBuffer, meta_wayland_dma_buf_buffer, G_TYPE_OBJECT); gboolean meta_wayland_dma_buf_buffer_attach (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; CoglPixelFormat cogl_format; EGLImageKHR egl_image; CoglTexture2D *texture; EGLint attribs[64]; int attr_idx = 0; if (buffer->texture) return TRUE; switch (dma_buf->drm_format) { 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; } attribs[attr_idx++] = EGL_WIDTH; attribs[attr_idx++] = dma_buf->width; attribs[attr_idx++] = EGL_HEIGHT; attribs[attr_idx++] = dma_buf->height; attribs[attr_idx++] = EGL_LINUX_DRM_FOURCC_EXT; attribs[attr_idx++] = dma_buf->drm_format; attribs[attr_idx++] = EGL_DMA_BUF_PLANE0_FD_EXT; attribs[attr_idx++] = dma_buf->fds[0]; attribs[attr_idx++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; attribs[attr_idx++] = dma_buf->offsets[0]; attribs[attr_idx++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; attribs[attr_idx++] = dma_buf->strides[0]; attribs[attr_idx++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; attribs[attr_idx++] = dma_buf->drm_modifier & 0xffffffff; attribs[attr_idx++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; attribs[attr_idx++] = dma_buf->drm_modifier >> 32; if (dma_buf->fds[1] >= 0) { attribs[attr_idx++] = EGL_DMA_BUF_PLANE1_FD_EXT; attribs[attr_idx++] = dma_buf->fds[1]; attribs[attr_idx++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT; attribs[attr_idx++] = dma_buf->offsets[1]; attribs[attr_idx++] = EGL_DMA_BUF_PLANE1_PITCH_EXT; attribs[attr_idx++] = dma_buf->strides[1]; attribs[attr_idx++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT; attribs[attr_idx++] = dma_buf->drm_modifier & 0xffffffff; attribs[attr_idx++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT; attribs[attr_idx++] = dma_buf->drm_modifier >> 32; } if (dma_buf->fds[2] >= 0) { attribs[attr_idx++] = EGL_DMA_BUF_PLANE2_FD_EXT; attribs[attr_idx++] = dma_buf->fds[2]; attribs[attr_idx++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT; attribs[attr_idx++] = dma_buf->offsets[2]; attribs[attr_idx++] = EGL_DMA_BUF_PLANE2_PITCH_EXT; attribs[attr_idx++] = dma_buf->strides[2]; attribs[attr_idx++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT; attribs[attr_idx++] = dma_buf->drm_modifier & 0xffffffff; attribs[attr_idx++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT; attribs[attr_idx++] = dma_buf->drm_modifier >> 32; } if (dma_buf->fds[3] >= 0) { attribs[attr_idx++] = EGL_DMA_BUF_PLANE3_FD_EXT; attribs[attr_idx++] = dma_buf->fds[3]; attribs[attr_idx++] = EGL_DMA_BUF_PLANE3_OFFSET_EXT; attribs[attr_idx++] = dma_buf->offsets[3]; attribs[attr_idx++] = EGL_DMA_BUF_PLANE3_PITCH_EXT; attribs[attr_idx++] = dma_buf->strides[3]; attribs[attr_idx++] = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT; attribs[attr_idx++] = dma_buf->drm_modifier & 0xffffffff; attribs[attr_idx++] = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT; attribs[attr_idx++] = dma_buf->drm_modifier >> 32; } attribs[attr_idx++] = EGL_NONE; attribs[attr_idx++] = EGL_NONE; /* The EXT_image_dma_buf_import spec states that EGL_NO_CONTEXT is to be used * in conjunction with the EGL_LINUX_DMA_BUF_EXT target. Similarly, the * native buffer is named in the attribs. */ egl_image = meta_egl_create_image (egl, egl_display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attribs, error); if (egl_image == EGL_NO_IMAGE_KHR) return FALSE; texture = cogl_egl_texture_2d_new_from_image (cogl_context, dma_buf->width, dma_buf->height, cogl_format, egl_image, error); meta_egl_destroy_image (egl, egl_display, egl_image, NULL); if (!texture) return FALSE; buffer->texture = COGL_TEXTURE (texture); buffer->is_y_inverted = dma_buf->is_y_inverted; 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, }; 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); if (!meta_wayland_buffer_attach (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 || num_modifiers == 0) 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); } 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; } ukwm/src/wayland/meta-wayland-pointer-gestures.c0000664000175000017500000000524613233511035020760 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author: Carlos Garnacho */ #include "config.h" #include #include "meta-wayland-pointer-gestures.h" #include "pointer-gestures-unstable-v1-server-protocol.h" #include "meta-wayland-versions.h" #include "meta-wayland-private.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); } ukwm/src/wayland/meta-wayland-tablet-pad-group.h0000664000175000017500000000622513220600404020606 0ustar fengfeng/* * 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 "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 */ ukwm/src/wayland/meta-wayland-buffer.h0000664000175000017500000000522513233511035016714 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_WAYLAND_BUFFER_H #define META_WAYLAND_BUFFER_H #include #include #include #include "meta-wayland-types.h" #include "meta-wayland-egl-stream.h" #include "meta-wayland-dma-buf.h" typedef enum _MetaWaylandBufferType { META_WAYLAND_BUFFER_TYPE_UNKNOWN, META_WAYLAND_BUFFER_TYPE_SHM, META_WAYLAND_BUFFER_TYPE_EGL_IMAGE, META_WAYLAND_BUFFER_TYPE_EGL_STREAM, META_WAYLAND_BUFFER_TYPE_DMA_BUF, } MetaWaylandBufferType; struct _MetaWaylandBuffer { GObject parent; struct wl_resource *resource; struct wl_listener destroy_listener; CoglTexture *texture; gboolean is_y_inverted; MetaWaylandBufferType type; struct { MetaWaylandEglStream *stream; } egl_stream; struct { MetaWaylandDmaBufBuffer *dma_buf; } 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); gboolean meta_wayland_buffer_attach (MetaWaylandBuffer *buffer, GError **error); CoglTexture * meta_wayland_buffer_get_texture (MetaWaylandBuffer *buffer); 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, cairo_region_t *region); #endif /* META_WAYLAND_BUFFER_H */ ukwm/src/wayland/meta-wayland-tablet-manager.h0000664000175000017500000000426513220600404020324 0ustar fengfeng/* * 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 "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 */ ukwm/src/wayland/meta-wayland-egl-stream.h0000664000175000017500000000372013233511035017501 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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" #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 (void); gboolean meta_wayland_egl_stream_is_y_inverted (MetaWaylandEglStream *stream); #endif /* META_WAYLAND_EGL_STREAM_H */ ukwm/src/wayland/meta-wayland-types.h0000664000175000017500000000516013233511035016605 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 _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 _MetaWaylandOutput MetaWaylandOutput; typedef struct _MetaWaylandSerial MetaWaylandSerial; typedef struct _MetaWaylandPointerClient MetaWaylandPointerClient; #endif ukwm/src/wayland/meta-wayland-tablet.c0000664000175000017500000000721713233511035016714 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author: Carlos Garnacho */ #define _GNU_SOURCE #include "config.h" #include #include #include "tablet-unstable-v2-server-protocol.h" #include "meta-surface-actor-wayland.h" #include "meta-wayland-private.h" #include "meta-wayland-tablet.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; guint vid, pid; zwp_tablet_v2_send_name (resource, clutter_input_device_get_device_name (device)); zwp_tablet_v2_send_path (resource, clutter_input_device_get_device_node (device)); if (sscanf (clutter_input_device_get_vendor_id (device), "%x", &vid) == 1 && sscanf (clutter_input_device_get_product_id (device), "%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); } ukwm/src/wayland/meta-wayland-popup.c0000664000175000017500000002262213220600404016574 0ustar fengfeng/* * 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 "meta-wayland-popup.h" #include "meta-wayland-pointer.h" #include "meta-wayland-private.h" #include "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 = surface->window; meta_wayland_pointer_start_grab (pointer, (MetaWaylandPointerGrab*)grab); meta_display_begin_grab_op (window->display, window->screen, 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; } ukwm/src/wayland/meta-wayland-xdg-foreign.h0000664000175000017500000000214313233511035017650 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 */ ukwm/src/wayland/meta-wayland-seat.c0000664000175000017500000003554313233511035016400 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "config.h" #include "meta-wayland-seat.h" #include "meta-wayland-private.h" #include "meta-wayland-versions.h" #include "meta-wayland-data-device.h" #include "meta-wayland-tablet-seat.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 const struct wl_seat_interface seat_interface = { seat_get_pointer, seat_get_keyboard, seat_get_touch }; 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 (ClutterDeviceManager *device_manager) { const GSList *devices, *l; uint32_t capabilities = 0; devices = clutter_device_manager_peek_devices (device_manager); 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; } } 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, ClutterDeviceManager *device_manager) { uint32_t capabilities; capabilities = lookup_device_capabilities (device_manager); meta_wayland_seat_set_capabilities (seat, capabilities); } static void meta_wayland_seat_devices_updated (ClutterDeviceManager *device_manager, ClutterInputDevice *input_device, MetaWaylandSeat *seat) { meta_wayland_seat_update_capabilities (seat, device_manager); } static MetaWaylandSeat * meta_wayland_seat_new (MetaWaylandCompositor *compositor, struct wl_display *display) { MetaWaylandSeat *seat = g_new0 (MetaWaylandSeat, 1); ClutterDeviceManager *device_manager; 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); meta_wayland_data_device_init (&seat->data_device); device_manager = clutter_device_manager_get_default (); meta_wayland_seat_update_capabilities (seat, device_manager); g_signal_connect (device_manager, "device-added", G_CALLBACK (meta_wayland_seat_devices_updated), seat); g_signal_connect (device_manager, "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) { ClutterDeviceManager *device_manager; device_manager = clutter_device_manager_get_default (); g_signal_handlers_disconnect_by_data (device_manager, seat); meta_wayland_seat_set_capabilities (seat, 0); g_object_unref (seat->pointer); g_object_unref (seat->keyboard); g_object_unref (seat->touch); g_slice_free (MetaWaylandSeat, 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 (!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 (!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); case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: if (meta_wayland_seat_has_keyboard (seat)) return meta_wayland_keyboard_handle_event (seat->keyboard, (const ClutterKeyEvent *) event); 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); 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); } tablet_seat = meta_wayland_tablet_manager_ensure_seat (compositor->tablet_manager, seat); meta_wayland_tablet_seat_set_pad_focus (tablet_seat, 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) { 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)); } 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; } ukwm/src/wayland/meta-wayland-tablet-tool.h0000664000175000017500000000664713220600404017675 0ustar fengfeng/* * 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 "meta-wayland-types.h" #include "meta-cursor-renderer.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; MetaCursorSprite *default_sprite; guint 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); #endif /* META_WAYLAND_TABLET_TOOL_H */ ukwm/src/wayland/meta-wayland-pointer-gesture-pinch.h0000664000175000017500000000304113220600404021663 0ustar fengfeng/* * 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 #include "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 */ ukwm/src/wayland/meta-window-wayland.h0000664000175000017500000000700413233511035016747 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_WINDOW_WAYLAND_H #define META_WINDOW_WAYLAND_H #include "core/window-private.h" #include #include "wayland/meta-wayland-types.h" G_BEGIN_DECLS #define META_TYPE_WINDOW_WAYLAND (meta_window_wayland_get_type()) #define META_WINDOW_WAYLAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_WINDOW_WAYLAND, MetaWindowWayland)) #define META_WINDOW_WAYLAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_WINDOW_WAYLAND, MetaWindowWaylandClass)) #define META_IS_WINDOW_WAYLAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_WINDOW_WAYLAND)) #define META_IS_WINDOW_WAYLAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_WINDOW_WAYLAND)) #define META_WINDOW_WAYLAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_WINDOW_WAYLAND, MetaWindowWaylandClass)) GType meta_window_wayland_get_type (void); typedef struct _MetaWindowWayland MetaWindowWayland; typedef struct _MetaWindowWaylandClass MetaWindowWaylandClass; MetaWindow * meta_window_wayland_new (MetaDisplay *display, MetaWaylandSurface *surface); void meta_window_wayland_move_resize (MetaWindow *window, MetaWaylandSerial *acked_configure_serial, MetaRectangle new_geom, int dx, int dy); 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_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 ukwm/src/wayland/meta-wayland-xdg-shell.c0000664000175000017500000020173713233511035017333 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * */ #include "config.h" #include "wayland/meta-wayland-xdg-shell.h" #include "backends/meta-logical-monitor.h" #include "core/window-private.h" #include "wayland/meta-wayland.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-surface.h" #include "wayland/meta-wayland-versions.h" #include "wayland/meta-window-wayland.h" #include "xdg-shell-unstable-v6-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; } MetaWaylandXdgPositioner; typedef struct _MetaWaylandXdgSurfaceConstructor { MetaWaylandSurface *surface; struct wl_resource *resource; MetaWaylandXdgShellClient *shell_client; } MetaWaylandXdgSurfaceConstructor; typedef struct _MetaWaylandXdgSurfacePrivate { struct wl_resource *resource; MetaWaylandXdgShellClient *shell_client; MetaWaylandSerial acked_configure_serial; 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_SURFACE_ROLE_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; 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 (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); static struct wl_resource * meta_wayland_xdg_surface_get_shell_resource (MetaWaylandXdgSurface *xdg_surface); static MetaRectangle meta_wayland_xdg_surface_get_window_geometry (MetaWaylandXdgSurface *xdg_surface); static uint32_t meta_wayland_xdg_surface_send_configure (MetaWaylandXdgSurface *xdg_surface); 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 xdg_toplevel_destructor (struct wl_resource *resource) { MetaWaylandXdgToplevel *xdg_toplevel = wl_resource_get_user_data (resource); MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); meta_wayland_surface_destroy_window (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; if (parent_resource) { MetaWaylandSurface *parent_surface = surface_from_xdg_surface_resource (parent_resource); transient_for = parent_surface->window; } meta_window_set_transient_for (surface->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); if (!g_utf8_validate (title, -1, NULL)) title = ""; meta_window_set_title (surface->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); if (!g_utf8_validate (app_id, -1, NULL)) app_id = ""; meta_window_set_wm_class (surface->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); int monitor_scale; if (!meta_wayland_seat_get_grab_info (seat, surface, serial, FALSE, NULL, NULL)) return; monitor_scale = surface->window->monitor->scale; meta_window_show_menu (surface->window, META_WINDOW_MENU_WM, surface->window->buffer_rect.x + (x * monitor_scale), surface->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); 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_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 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); 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_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); 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; } surface->pending->has_new_max_size = TRUE; surface->pending->new_max_width = width; surface->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); 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; } surface->pending->has_new_min_size = TRUE; surface->pending->new_min_width = width; surface->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); meta_window_force_placement (surface->window); meta_window_maximize (surface->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); meta_window_unmaximize (surface->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); if (output_resource) { MetaWaylandOutput *output = wl_resource_get_user_data (output_resource); if (output) meta_window_move_to_monitor (surface->window, output->logical_monitor->number); } meta_window_make_fullscreen (surface->window); } static void xdg_toplevel_unset_fullscreen (struct wl_client *client, struct wl_resource *resource) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); meta_window_unmake_fullscreen (surface->window); } static void xdg_toplevel_set_minimized (struct wl_client *client, struct wl_resource *resource) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); meta_window_minimize (surface->window); } static const struct zxdg_toplevel_v6_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 xdg_popup_destructor (struct wl_resource *resource) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (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 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; MetaWaylandSurface *top_popup; 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; } top_popup = meta_wayland_pointer_get_top_popup (seat->pointer); if ((top_popup == NULL && !META_IS_WAYLAND_XDG_SURFACE (parent_surface->role)) || (top_popup != NULL && parent_surface != top_popup)) { MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_popup); struct wl_resource *xdg_shell_resource = meta_wayland_xdg_surface_get_shell_resource (xdg_surface); wl_resource_post_error (xdg_shell_resource, ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP, "parent not top most surface"); return; } xdg_popup->setup.grab_seat = seat; xdg_popup->setup.grab_serial = serial; } static const struct zxdg_popup_v6_interface meta_wayland_xdg_popup_interface = { xdg_popup_destroy, xdg_popup_grab, }; static void handle_popup_parent_destroyed (struct wl_listener *listener, void *data) { MetaWaylandXdgPopup *xdg_popup = wl_container_of (listener, xdg_popup, parent_destroy_listener); MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_popup); struct wl_resource *xdg_shell_resource = meta_wayland_xdg_surface_get_shell_resource (xdg_surface); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (xdg_popup); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); 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_surface_destroy_window (surface); } static void fill_states (struct wl_array *states, MetaWindow *window) { uint32_t *s; if (META_WINDOW_MAXIMIZED (window)) { s = wl_array_add (states, sizeof *s); *s = ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED; } if (meta_window_is_fullscreen (window)) { s = wl_array_add (states, sizeof *s); *s = ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN; } if (meta_grab_op_is_resizing (window->display->grab_op)) { s = wl_array_add (states, sizeof *s); *s = ZXDG_TOPLEVEL_V6_STATE_RESIZING; } if (meta_window_appears_focused (window)) { s = wl_array_add (states, sizeof *s); *s = ZXDG_TOPLEVEL_V6_STATE_ACTIVATED; } } static void meta_wayland_xdg_toplevel_send_configure (MetaWaylandXdgToplevel *xdg_toplevel, int new_width, int new_height, MetaWaylandSerial *sent_serial) { MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_toplevel); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (xdg_toplevel); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); struct wl_array states; uint32_t serial; wl_array_init (&states); fill_states (&states, surface->window); zxdg_toplevel_v6_send_configure (xdg_toplevel->resource, new_width, new_height, &states); wl_array_release (&states); serial = meta_wayland_xdg_surface_send_configure (xdg_surface); if (sent_serial) { sent_serial->set = TRUE; sent_serial->value = serial; } } static gboolean is_new_size_hints_valid (MetaWindow *window, MetaWaylandPendingState *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 xdg_toplevel_role_commit (MetaWaylandSurfaceRole *surface_role, MetaWaylandPendingState *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); MetaWaylandSurfaceRoleClass *surface_role_class; MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWindow *window = surface->window; MetaRectangle window_geometry; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_xdg_toplevel_parent_class); surface_role_class->commit (surface_role, pending); if (!xdg_surface_priv->configure_sent) { meta_wayland_xdg_toplevel_send_configure (xdg_toplevel, 0, 0, NULL); return; } if (!pending->newly_attached) return; /* If the window disappeared the surface is not coming back. */ if (!window) return; if (pending->has_new_geometry) { window_geometry = meta_wayland_xdg_surface_get_window_geometry (xdg_surface); meta_window_wayland_move_resize (window, &xdg_surface_priv->acked_configure_serial, window_geometry, pending->dx, pending->dy); } else if (pending->dx != 0 || pending->dx != 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"); } } xdg_surface_priv->acked_configure_serial.set = FALSE; } static MetaWaylandSurface * xdg_toplevel_role_get_toplevel (MetaWaylandSurfaceRole *surface_role) { return meta_wayland_surface_role_get_surface (surface_role); } static void xdg_toplevel_role_configure (MetaWaylandSurfaceRoleShellSurface *shell_surface_role, int new_x, int new_y, int new_width, int new_height, MetaWaylandSerial *sent_serial) { MetaWaylandXdgToplevel *xdg_toplevel = META_WAYLAND_XDG_TOPLEVEL (shell_surface_role); 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, new_width, new_height, sent_serial); } static void xdg_toplevel_role_managed (MetaWaylandSurfaceRoleShellSurface *shell_surface_role, MetaWindow *window) { } static void xdg_toplevel_role_close (MetaWaylandSurfaceRoleShellSurface *shell_surface_role) { MetaWaylandXdgToplevel *xdg_toplevel = META_WAYLAND_XDG_TOPLEVEL (shell_surface_role); zxdg_toplevel_v6_send_close (xdg_toplevel->resource); } static void xdg_toplevel_role_shell_client_destroyed (MetaWaylandXdgSurface *xdg_surface) { MetaWaylandXdgToplevel *xdg_toplevel = META_WAYLAND_XDG_TOPLEVEL (xdg_surface); struct wl_resource *xdg_shell_resource = meta_wayland_xdg_surface_get_shell_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_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 xdg_toplevel_role_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; MetaWaylandSurfaceRoleShellSurfaceClass *shell_surface_role_class; MetaWaylandXdgSurfaceClass *xdg_surface_class; object_class = G_OBJECT_CLASS (klass); object_class->finalize = xdg_toplevel_role_finalize; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); surface_role_class->commit = xdg_toplevel_role_commit; surface_role_class->get_toplevel = xdg_toplevel_role_get_toplevel; shell_surface_role_class = META_WAYLAND_SURFACE_ROLE_SHELL_SURFACE_CLASS (klass); shell_surface_role_class->configure = xdg_toplevel_role_configure; shell_surface_role_class->managed = xdg_toplevel_role_managed; shell_surface_role_class->close = xdg_toplevel_role_close; xdg_surface_class = META_WAYLAND_XDG_SURFACE_CLASS (klass); xdg_surface_class->shell_client_destroyed = xdg_toplevel_role_shell_client_destroyed; } static void scale_placement_rule (MetaPlacementRule *placement_rule, MetaWaylandSurface *surface) { int geometry_scale; geometry_scale = meta_window_wayland_get_geometry_scale (surface->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 (MetaWaylandXdgPopup *xdg_popup) { MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (xdg_popup); 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 (seat) { if (!meta_wayland_seat_can_popup (seat, serial)) { zxdg_popup_v6_send_popup_done (xdg_popup->resource); 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_surface_set_window (surface, window); meta_window_update_monitor (window, FALSE); 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_surface_destroy_window (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 xdg_popup_role_commit (MetaWaylandSurfaceRole *surface_role, MetaWaylandPendingState *pending) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (surface_role); MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (surface_role); MetaWaylandSurfaceRoleClass *surface_role_class; MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaRectangle window_geometry; if (xdg_popup->setup.parent_surface) finish_popup_setup (xdg_popup); surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_xdg_popup_parent_class); surface_role_class->commit (surface_role, pending); /* If the window disappeared the surface is not coming back. */ if (!surface->window) return; if (!pending->newly_attached) return; if (!surface->buffer_ref.buffer) return; window_geometry = meta_wayland_xdg_surface_get_window_geometry (xdg_surface); meta_window_wayland_move_resize (surface->window, NULL, window_geometry, pending->dx, pending->dy); } static MetaWaylandSurface * xdg_popup_role_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 xdg_popup_role_configure (MetaWaylandSurfaceRoleShellSurface *shell_surface_role, int new_x, int new_y, int new_width, int new_height, MetaWaylandSerial *sent_serial) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (shell_surface_role); MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_popup); MetaWindow *parent_window = xdg_popup->parent_surface->window; 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 = (new_x - parent_window->rect.x) / geometry_scale; y = (new_y - parent_window->rect.y) / geometry_scale; zxdg_popup_v6_send_configure (xdg_popup->resource, x, y, new_width, new_height); meta_wayland_xdg_surface_send_configure (xdg_surface); } static void xdg_popup_role_managed (MetaWaylandSurfaceRoleShellSurface *shell_surface_role, MetaWindow *window) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (shell_surface_role); MetaWaylandSurface *parent = xdg_popup->parent_surface; g_assert (parent); meta_window_set_transient_for (window, parent->window); meta_window_set_type (window, META_WINDOW_DROPDOWN_MENU); } static void xdg_popup_role_shell_client_destroyed (MetaWaylandXdgSurface *xdg_surface) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (xdg_surface); struct wl_resource *xdg_shell_resource = meta_wayland_xdg_surface_get_shell_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_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_xdg_popup_done (MetaWaylandPopupSurface *popup_surface) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (popup_surface); zxdg_popup_v6_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_shell_resource = meta_wayland_xdg_surface_get_shell_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_shell_resource, ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP, "destroyed popup not top most popup"); } xdg_popup->popup = NULL; meta_wayland_surface_destroy_window (surface); } 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 xdg_popup_role_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; MetaWaylandSurfaceRoleShellSurfaceClass *shell_surface_role_class; MetaWaylandXdgSurfaceClass *xdg_surface_class; object_class = G_OBJECT_CLASS (klass); object_class->finalize = xdg_popup_role_finalize; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); surface_role_class->commit = xdg_popup_role_commit; surface_role_class->get_toplevel = xdg_popup_role_get_toplevel; shell_surface_role_class = META_WAYLAND_SURFACE_ROLE_SHELL_SURFACE_CLASS (klass); shell_surface_role_class->configure = xdg_popup_role_configure; shell_surface_role_class->managed = xdg_popup_role_managed; xdg_surface_class = META_WAYLAND_XDG_SURFACE_CLASS (klass); xdg_surface_class->shell_client_destroyed = xdg_popup_role_shell_client_destroyed; } static struct wl_resource * meta_wayland_xdg_surface_get_shell_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 uint32_t meta_wayland_xdg_surface_send_configure (MetaWaylandXdgSurface *xdg_surface) { MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); struct wl_display *display; uint32_t serial; display = wl_client_get_display (wl_resource_get_client (priv->resource)); serial = wl_display_next_serial (display); zxdg_surface_v6_send_configure (priv->resource, serial); priv->configure_sent = TRUE; return serial; } static void xdg_surface_destructor (struct wl_resource *resource) { MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource); MetaWaylandXdgSurface *xdg_surface = wl_resource_get_user_data (resource); MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); meta_wayland_compositor_destroy_frame_callbacks (surface->compositor, 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_shell_resource = meta_wayland_xdg_surface_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 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, ZXDG_SHELL_V6_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); surface->pending->has_new_geometry = TRUE; surface->pending->new_geometry.x = x; surface->pending->new_geometry.y = y; surface->pending->new_geometry.width = width; surface->pending->new_geometry.height = height; } static void xdg_surface_ack_configure (struct wl_client *client, struct wl_resource *resource, uint32_t serial) { MetaWaylandXdgSurface *xdg_surface = wl_resource_get_user_data (resource); MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); priv->acked_configure_serial.set = TRUE; priv->acked_configure_serial.value = serial; } static const struct zxdg_surface_v6_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 xdg_surface_role_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 xdg_surface_role_commit (MetaWaylandSurfaceRole *surface_role, MetaWaylandPendingState *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 = surface->window; MetaWaylandSurfaceRoleClass *surface_role_class; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_xdg_surface_parent_class); surface_role_class->commit (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; else return; if (pending->has_new_geometry) { /* If we have new geometry, use it. */ priv->geometry = pending->new_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_surface_calculate_window_geometry (surface, &new_geometry, 0, 0); if (!meta_rectangle_equal (&new_geometry, &priv->geometry)) { pending->has_new_geometry = TRUE; priv->geometry = new_geometry; } } } static void xdg_surface_role_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_shell_resource = meta_wayland_xdg_surface_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_xdg_surface_parent_class); surface_role_class->assigned (surface_role); } static void xdg_surface_role_ping (MetaWaylandSurfaceRoleShellSurface *shell_surface_role, uint32_t serial) { MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (shell_surface_role); MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); zxdg_shell_v6_send_ping (priv->shell_client->resource, serial); } static void xdg_surface_role_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, 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_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; MetaWaylandSurfaceRoleShellSurfaceClass *shell_surface_role_class; GParamSpec *pspec; object_class = G_OBJECT_CLASS (klass); object_class->finalize = xdg_surface_role_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->commit = xdg_surface_role_commit; surface_role_class->assigned = xdg_surface_role_assigned; shell_surface_role_class = META_WAYLAND_SURFACE_ROLE_SHELL_SURFACE_CLASS (klass); shell_surface_role_class->ping = xdg_surface_role_ping; klass->shell_client_destroyed = xdg_surface_role_shell_client_destroyed; 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, ZXDG_SURFACE_V6_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; 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, ZXDG_SHELL_V6_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, &zxdg_toplevel_v6_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); meta_wayland_surface_set_window (surface, window); meta_window_update_monitor (window, FALSE); } 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_shell_resource = constructor->shell_client->resource; struct wl_resource *xdg_surface_resource = constructor->resource; MetaWaylandSurface *parent_surface = surface_from_xdg_surface_resource (parent_resource); MetaWaylandXdgPositioner *xdg_positioner; MetaWaylandXdgPopup *xdg_popup; MetaWaylandXdgSurface *xdg_surface; 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_shell_resource, ZXDG_SHELL_V6_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, &zxdg_popup_v6_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); 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, ZXDG_SURFACE_V6_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, ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, "xdg_surface::ack_configure called before constructed"); } static const struct zxdg_surface_v6_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 MetaPlacementRule meta_wayland_xdg_positioner_to_placement (MetaWaylandXdgPositioner *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 meta_wayland_xdg_positioner_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void meta_wayland_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, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid size"); return; } positioner->width = width; positioner->height = height; } static void meta_wayland_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, 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 meta_wayland_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 & 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 meta_wayland_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 & 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 meta_wayland_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 = (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 meta_wayland_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 const struct zxdg_positioner_v6_interface meta_wayland_xdg_positioner_interface = { meta_wayland_xdg_positioner_destroy, meta_wayland_xdg_positioner_set_size, meta_wayland_xdg_positioner_set_anchor_rect, meta_wayland_xdg_positioner_set_anchor, meta_wayland_xdg_positioner_set_gravity, meta_wayland_xdg_positioner_set_constraint_adjustment, meta_wayland_xdg_positioner_set_offset, }; static void xdg_positioner_destructor (struct wl_resource *resource) { MetaWaylandXdgPositioner *positioner = wl_resource_get_user_data (resource); g_free (positioner); } static void xdg_shell_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, ZXDG_SHELL_V6_ERROR_DEFUNCT_SURFACES, "xdg_shell destroyed before its surfaces"); wl_resource_destroy (resource); } static void xdg_shell_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, &zxdg_positioner_v6_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_shell_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, ZXDG_SHELL_V6_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, WL_DISPLAY_ERROR_INVALID_OBJECT, "xdg_shell::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 (MetaWaylandXdgSurfaceConstructor, 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_xdg_surface_constructor_interface, constructor, xdg_surface_constructor_destructor); shell_client->surface_constructors = g_list_append (shell_client->surface_constructors, constructor); } static void xdg_shell_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_xdg_shell_interface = { xdg_shell_destroy, xdg_shell_create_positioner, xdg_shell_get_xdg_surface, xdg_shell_pong, }; static void xdg_shell_destructor (struct wl_resource *resource) { MetaWaylandXdgShellClient *shell_client = wl_resource_get_user_data (resource); 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 bind_xdg_shell (struct wl_client *client, void *data, guint32 version, guint32 id) { MetaWaylandXdgShellClient *shell_client; shell_client = g_new0 (MetaWaylandXdgShellClient, 1); shell_client->resource = wl_resource_create (client, &zxdg_shell_v6_interface, version, id); wl_resource_set_implementation (shell_client->resource, &meta_wayland_xdg_shell_interface, shell_client, xdg_shell_destructor); } void meta_wayland_xdg_shell_init (MetaWaylandCompositor *compositor) { if (wl_global_create (compositor->wayland_display, &zxdg_shell_v6_interface, META_XDG_SHELL_VERSION, compositor, bind_xdg_shell) == NULL) g_error ("Failed to register a global xdg-shell object"); } ukwm/src/wayland/protocol/0000775000175000017500000000000013220600404014541 5ustar fengfengukwm/src/wayland/protocol/gtk-primary-selection.xml0000664000175000017500000002371113220600404021520 0ustar fengfeng 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. ukwm/src/wayland/protocol/gtk-shell.xml0000664000175000017500000000457313220600404017166 0ustar fengfeng gtk_shell is a protocol extension providing additional features for clients implementing it. ukwm/src/wayland/meta-xwayland.c0000664000175000017500000004755313233511035015642 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * X Wayland Support * * Copyright (C) 2013 Intel Corporation * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "config.h" #include "meta-xwayland.h" #include "meta-xwayland-private.h" #include #include #include #include #include #include "compositor/meta-surface-actor-wayland.h" enum { XWAYLAND_SURFACE_WINDOW_ASSOCIATED, XWAYLAND_SURFACE_LAST_SIGNAL }; guint xwayland_surface_signals[XWAYLAND_SURFACE_LAST_SIGNAL]; #define META_TYPE_WAYLAND_SURFACE_ROLE_XWAYLAND (meta_wayland_surface_role_xwayland_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandSurfaceRoleXWayland, meta_wayland_surface_role_xwayland, META, WAYLAND_SURFACE_ROLE_XWAYLAND, MetaWaylandSurfaceRole); struct _MetaWaylandSurfaceRoleXWayland { MetaWaylandSurfaceRole parent; }; G_DEFINE_TYPE (MetaWaylandSurfaceRoleXWayland, meta_wayland_surface_role_xwayland, META_TYPE_WAYLAND_SURFACE_ROLE); static void associate_window_with_surface (MetaWindow *window, MetaWaylandSurface *surface) { MetaDisplay *display = window->display; if (!meta_wayland_surface_assign_role (surface, META_TYPE_WAYLAND_SURFACE_ROLE_XWAYLAND, 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; } window->surface = surface; meta_wayland_surface_set_window (surface, window); g_signal_emit (surface->role, xwayland_surface_signals[XWAYLAND_SURFACE_WINDOW_ASSOCIATED], 0); meta_compositor_window_surface_changed (display->compositor, 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; /* 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) { meta_wayland_surface_set_window (window->surface, NULL); window->surface = NULL; } resource = wl_client_get_object (manager->client, surface_id); if (resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (resource); associate_window_with_surface (window, surface); return TRUE; } else return FALSE; } typedef struct { MetaXWaylandManager *manager; MetaWindow *window; guint32 surface_id; guint later_id; } AssociateWindowWithSurfaceOp; static void associate_window_with_surface_window_unmanaged (MetaWindow *window, AssociateWindowWithSurfaceOp *op); static void associate_window_with_surface_op_free (AssociateWindowWithSurfaceOp *op) { if (op->later_id != 0) meta_later_remove (op->later_id); g_signal_handlers_disconnect_by_func (op->window, (gpointer) associate_window_with_surface_window_unmanaged, op); g_free (op); } static void associate_window_with_surface_window_unmanaged (MetaWindow *window, AssociateWindowWithSurfaceOp *op) { associate_window_with_surface_op_free (op); } static gboolean associate_window_with_surface_later (gpointer user_data) { AssociateWindowWithSurfaceOp *op = user_data; op->later_id = 0; if (!associate_window_with_surface_id (op->manager, op->window, op->surface_id)) { /* Not here? Oh well... nothing we can do */ g_warning ("Unknown surface ID %d (from window %s)", op->surface_id, op->window->desc); } associate_window_with_surface_op_free (op); return G_SOURCE_REMOVE; } 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... it should arrive after the next * iteration through the loop, so queue a later and see * what happens. */ AssociateWindowWithSurfaceOp *op = g_new0 (AssociateWindowWithSurfaceOp, 1); op->manager = manager; op->window = window; op->surface_id = surface_id; op->later_id = meta_later_add (META_LATER_BEFORE_REDRAW, associate_window_with_surface_later, op, NULL); g_signal_connect (op->window, "unmanaged", G_CALLBACK (associate_window_with_surface_window_unmanaged), op); } } 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); GError *error = NULL; if (!g_subprocess_wait_finish (proc, result, &error)) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_error ("Failed to finish waiting for Xwayland: %s", error->message); g_clear_error (&error); } else if (!g_subprocess_get_successful (proc)) g_error ("X Wayland crashed; aborting"); else { /* For now we simply abort if we see the server exit. * * In the future X will only be loaded lazily for legacy X support * but for now it's a hard requirement. */ g_error ("Spurious exit of X Wayland server"); } } static int x_io_error (Display *display) { g_error ("Connection to xwayland lost"); return 0; } static gboolean choose_xdisplay (MetaXWaylandManager *manager) { int display = 0; char *lock_file = NULL; gboolean fatal = FALSE; /* Hack to keep the unused Xwayland instance on * the login screen from taking the prime :0 display * number. */ if (g_getenv ("RUNNING_UNDER_GDM") != NULL) display = 1024; do { lock_file = create_lock_file (display, &display); if (!lock_file) { g_warning ("Failed to create an X lock file"); return FALSE; } manager->abstract_fd = bind_to_abstract_socket (display, &fatal); if (manager->abstract_fd < 0) { unlink (lock_file); if (!fatal) { display++; continue; } else { g_warning ("Failed to bind abstract socket"); return FALSE; } } manager->unix_fd = bind_to_unix_socket (display); if (manager->unix_fd < 0) { unlink (lock_file); close (manager->abstract_fd); return FALSE; } break; } while (1); manager->display_index = display; manager->display_name = g_strdup_printf (":%d", manager->display_index); manager->lock_file = lock_file; return TRUE; } static void xserver_finished_init (MetaXWaylandManager *manager) { /* At this point xwayland is all setup to start accepting * connections so we can quit the transient initialization mainloop * and unblock meta_wayland_init() to continue initializing ukwm. * */ g_main_loop_quit (manager->init_loop); g_clear_pointer (&manager->init_loop, g_main_loop_unref); } static gboolean on_displayfd_ready (int fd, GIOCondition condition, gpointer user_data) { MetaXWaylandManager *manager = 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. */ xserver_finished_init (manager); return G_SOURCE_REMOVE; } gboolean meta_xwayland_start (MetaXWaylandManager *manager, struct wl_display *wl_display) { int xwayland_client_fd[2]; int displayfd[2]; gboolean started = FALSE; g_autoptr(GSubprocessLauncher) launcher = NULL; GSubprocessFlags flags; GError *error = NULL; if (!choose_xdisplay (manager)) goto out; /* 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_warning ("xwayland_client_fd socketpair failed\n"); goto out; } if (socketpair (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, displayfd) < 0) { g_warning ("displayfd socketpair failed\n"); goto out; } /* 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->abstract_fd, 4); g_subprocess_launcher_take_fd (launcher, manager->unix_fd, 5); g_subprocess_launcher_take_fd (launcher, displayfd[1], 6); g_subprocess_launcher_setenv (launcher, "WAYLAND_SOCKET", "3", TRUE); /* Use the -terminate parameter to ensure that Xwayland exits cleanly * after the last client disconnects. Fortunately that includes the window * manager so it won't exit prematurely either. This ensures that Xwayland * won't try to reconnect and crash, leaving uninteresting core dumps. We do * want core dumps from Xwayland but only if a real bug occurs... */ manager->proc = g_subprocess_launcher_spawn (launcher, &error, XWAYLAND_PATH, manager->display_name, "-rootless", "-terminate", "-core", "-listen", "4", "-listen", "5", "-displayfd", "6", NULL); if (!manager->proc) { g_error ("Failed to spawn Xwayland: %s", error->message); goto out; } 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, manager); manager->client = wl_client_create (wl_display, xwayland_client_fd[0]); /* We need to run a mainloop until we know xwayland has a binding * for our xserver interface at which point we can assume it's * ready to start accepting connections. */ manager->init_loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (manager->init_loop); started = TRUE; out: if (!started) { unlink (manager->lock_file); g_clear_pointer (&manager->lock_file, g_free); } return started; } /* To be called right after connecting */ void meta_xwayland_complete_init (void) { /* 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); meta_xwayland_init_selection (); } void meta_xwayland_stop (MetaXWaylandManager *manager) { char path[256]; g_cancellable_cancel (manager->xserver_died_cancellable); meta_xwayland_shutdown_selection (); g_clear_object (&manager->proc); g_clear_object (&manager->xserver_died_cancellable); snprintf (path, sizeof path, "/tmp/.X11-unix/X%d", manager->display_index); unlink (path); g_clear_pointer (&manager->display_name, g_free); if (manager->lock_file) { unlink (manager->lock_file); g_clear_pointer (&manager->lock_file, g_free); } } static void xwayland_surface_assigned (MetaWaylandSurfaceRole *surface_role) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); /* See comment in xwayland_surface_commit for why we reply even though the * surface may not be drawn the next frame. */ wl_list_insert_list (&surface->compositor->frame_callbacks, &surface->pending_frame_callback_list); wl_list_init (&surface->pending_frame_callback_list); } static void xwayland_surface_commit (MetaWaylandSurfaceRole *surface_role, MetaWaylandPendingState *pending) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); /* For Xwayland windows, throttling frames when the window isn't actually * drawn is less useful, because Xwayland still has to do the drawing sent * from the application - the throttling would only be of sending us damage * messages, so we simplify and send frame callbacks after the next paint of * the screen, whether the window was drawn or not. * * Currently it may take a few frames before we draw the window, for not * completely understood reasons, and in that case, not thottling frame * callbacks to drawing has the happy side effect that we avoid showing the * user the initial black frame from when the window is mapped empty. */ meta_wayland_surface_queue_pending_state_frame_callbacks (surface, pending); } static MetaWaylandSurface * xwayland_surface_get_toplevel (MetaWaylandSurfaceRole *surface_role) { return meta_wayland_surface_role_get_surface (surface_role); } static void meta_wayland_surface_role_xwayland_init (MetaWaylandSurfaceRoleXWayland *role) { } static void meta_wayland_surface_role_xwayland_class_init (MetaWaylandSurfaceRoleXWaylandClass *klass) { MetaWaylandSurfaceRoleClass *surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); surface_role_class->assigned = xwayland_surface_assigned; surface_role_class->commit = xwayland_surface_commit; surface_role_class->get_toplevel = xwayland_surface_get_toplevel; xwayland_surface_signals[XWAYLAND_SURFACE_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); } ukwm/src/wayland/meta-wayland-region.c0000664000175000017500000000611613233511035016721 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "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; } ukwm/src/wayland/meta-wayland-pointer-gesture-swipe.c0000664000175000017500000001235713233511035021723 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author: Carlos Garnacho */ #include "config.h" #include #include "meta-wayland-pointer-gesture-swipe.h" #include "meta-wayland-pointer.h" #include "meta-wayland-seat.h" #include "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)); } ukwm/src/wayland/meta-wayland.h0000664000175000017500000000703413233511035015445 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_WAYLAND_H #define META_WAYLAND_H #include #include #include "meta-wayland-types.h" void meta_wayland_override_display_name (char *display_name); void meta_wayland_pre_clutter_init (void); void meta_wayland_init (void); void meta_wayland_finalize (void); /* We maintain a singleton MetaWaylandCompositor which can be got at via this * API after meta_wayland_init() has been called. */ 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_destroy_frame_callbacks (MetaWaylandCompositor *compositor, MetaWaylandSurface *surface); const char *meta_wayland_get_wayland_display_name (MetaWaylandCompositor *compositor); 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); #endif ukwm/src/wayland/meta-wayland-tablet-pad-strip.h0000664000175000017500000000444513220600404020615 0ustar fengfeng/* * 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 "meta-wayland-types.h" #include "meta-cursor-renderer.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 */ ukwm/src/wayland/meta-wayland-outputs.h0000664000175000017500000000315113233511035017162 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_WAYLAND_OUTPUTS_H #define META_WAYLAND_OUTPUTS_H #include "backends/meta-monitor-manager-private.h" #include "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; GList *resources; }; void meta_wayland_outputs_init (MetaWaylandCompositor *compositor); #endif /* META_WAYLAND_OUTPUTS_H */ ukwm/src/wayland/meta-wayland-region.h0000664000175000017500000000275213233511035016730 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_WAYLAND_REGION_H #define META_WAYLAND_REGION_H #include #include #include #include "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 */ ukwm/src/wayland/meta-wayland-inhibit-shortcuts.h0000664000175000017500000000271413233511035021125 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Olivier Fourdan */ #ifndef META_WAYLAND_INHIBIT_SHORTCUTS_H #define META_WAYLAND_INHIBIT_SHORTCUTS_H #include #include "wayland/meta-wayland-types.h" #include "meta/window.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 */ ukwm/src/wayland/meta-wayland-pointer.h0000664000175000017500000001350313220600404017114 0ustar fengfeng/* * 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-wayland-types.h" #include "meta-wayland-seat.h" #include "meta-wayland-pointer-gesture-swipe.h" #include "meta-wayland-pointer-gesture-pinch.h" #include "meta-wayland-surface.h" #include "meta-wayland-pointer-constraints.h" #include #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; guint 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 */ ukwm/src/wayland/meta-wayland-tablet-seat.h0000664000175000017500000000773313220600404017651 0ustar fengfeng/* * 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 "meta-wayland-types.h" struct _MetaWaylandTabletSeat { MetaWaylandTabletManager *manager; MetaWaylandSeat *seat; ClutterDeviceManager *device_manager; 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); #endif /* META_WAYLAND_TABLET_SEAT_H */ ukwm/src/wayland/meta-pointer-lock-wayland.c0000664000175000017500000000424113233511035020041 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #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; } ukwm/src/wayland/meta-wayland-tablet.h0000664000175000017500000000412713220600404016711 0ustar fengfeng/* * 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 "meta-wayland-types.h" #include "meta-cursor-renderer.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 */ ukwm/src/wayland/meta-wayland-input-device.c0000664000175000017500000000733513233511035020036 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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); } ukwm/src/wayland/meta-wayland-dma-buf.h0000664000175000017500000000332213233511035016752 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl * Daniel Stone */ #ifndef META_WAYLAND_DMA_BUF_H #define META_WAYLAND_DMA_BUF_H #include #include #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, GError **error); MetaWaylandDmaBufBuffer * meta_wayland_dma_buf_from_buffer (MetaWaylandBuffer *buffer); #endif /* META_WAYLAND_DMA_BUF_H */ ukwm/src/wayland/meta-wayland-tablet-pad.h0000664000175000017500000000605513220600404017455 0ustar fengfeng/* * 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 "meta-wayland-types.h" #include "meta-cursor-renderer.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 */ ukwm/src/wayland/meta-wayland-seat.h0000664000175000017500000000530313233511035016374 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef META_WAYLAND_SEAT_H #define META_WAYLAND_SEAT_H #include #include #include "meta-wayland-types.h" #include "meta-wayland-input-device.h" #include "meta-wayland-pointer.h" #include "meta-wayland-keyboard.h" #include "meta-wayland-touch.h" #include "meta-wayland-data-device.h" #include "meta-wayland-tablet-tool.h" struct _MetaWaylandSeat { struct wl_list base_resource_list; struct wl_display *wl_display; MetaWaylandPointer *pointer; MetaWaylandKeyboard *keyboard; MetaWaylandTouch *touch; MetaWaylandDataDevice data_device; 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 */ ukwm/src/wayland/meta-wayland-pointer.c0000664000175000017500000012167613220600404017122 0ustar fengfeng/* * 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 #include #include #include "meta-wayland-pointer.h" #include "meta-wayland-popup.h" #include "meta-wayland-private.h" #include "meta-wayland-seat.h" #include "meta-wayland-surface.h" #include "meta-wayland-buffer.h" #include "meta-wayland-surface-role-cursor.h" #include "meta-xwayland.h" #include "meta-cursor.h" #include "meta-cursor-tracker-private.h" #include "meta-surface-actor-wayland.h" #include "meta/meta-cursor-tracker.h" #include "backends/meta-backend-private.h" #include "backends/meta-cursor-tracker-private.h" #include "backends/meta-cursor-renderer.h" #include "relative-pointer-unstable-v1-server-protocol.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #endif #include #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 (); 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) { 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; if (!pointer->focus_client) return; if (!meta_backend_get_relative_motion_deltas (meta_get_backend (), event, &dx, &dy, &dx_unaccel, &dy_unaccel)) return; #ifdef HAVE_NATIVE_BACKEND time_us = clutter_evdev_event_get_time_usec (event); if (time_us == 0) #endif 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); } } 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 = clutter_evdev_event_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 (); 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; } if (meta_wayland_seat_has_pointer (seat)) 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); ClutterDeviceManager *manager; pointer->pointer_clients = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) meta_wayland_pointer_client_free); pointer->cursor_surface = NULL; manager = clutter_device_manager_get_default (); pointer->device = clutter_device_manager_get_core_device (manager, CLUTTER_POINTER_DEVICE); g_signal_connect (cursor_tracker, "cursor-changed", G_CALLBACK (meta_wayland_pointer_on_cursor_changed), pointer); } void meta_wayland_pointer_disable (MetaWaylandPointer *pointer) { MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); g_signal_handlers_disconnect_by_func (cursor_tracker, (gpointer) meta_wayland_pointer_on_cursor_changed, pointer); if (pointer->cursor_surface && pointer->cursor_surface_destroy_id) { g_signal_handler_disconnect (pointer->cursor_surface, pointer->cursor_surface_destroy_id); } 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_signal_handler_disconnect (pointer->current, pointer->current_surface_destroyed_handler_id); 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) actor = clutter_event_get_source (for_event); else actor = clutter_input_device_get_pointer_actor (pointer->device); 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); 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_signal_handler_disconnect (pointer->focus_surface, pointer->focus_surface_destroyed_handler_id); pointer->focus_surface_destroyed_handler_id = 0; pointer->focus_surface = NULL; } if (surface != NULL) { struct wl_client *client = wl_resource_get_client (surface->resource); ClutterPoint pos; 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_input_device_get_coords (pointer->device, NULL, &pos); if (pointer->focus_surface->window) meta_window_handle_enter (pointer->focus_surface->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_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) { repick_for_event (pointer, NULL); } void meta_wayland_pointer_get_relative_coordinates (MetaWaylandPointer *pointer, MetaWaylandSurface *surface, wl_fixed_t *sx, wl_fixed_t *sy) { float xf = 0.0f, yf = 0.0f; ClutterPoint pos; clutter_input_device_get_coords (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) { MetaWaylandSurfaceRoleCursor *cursor_role = META_WAYLAND_SURFACE_ROLE_CURSOR (pointer->cursor_surface->role); cursor_sprite = meta_wayland_surface_role_cursor_get_sprite (cursor_role); } 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_signal_handler_disconnect (prev_cursor_surface, pointer->cursor_surface_destroy_id); } 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_SURFACE_ROLE_CURSOR, 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 ()); MetaWaylandSurfaceRoleCursor *cursor_role; cursor_role = META_WAYLAND_SURFACE_ROLE_CURSOR (surface->role); meta_wayland_surface_role_cursor_set_renderer (cursor_role, cursor_renderer); meta_wayland_surface_role_cursor_set_hotspot (cursor_role, 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) { GList *l; if (pointer->focus_surface == surface) return TRUE; for (l = surface->subsurfaces; l; l = l->next) { MetaWaylandSurface *subsurface = l->data; 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); } ukwm/src/wayland/meta-wayland-data-device.c0000664000175000017500000017427313220600404017611 0ustar fengfeng/* * 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 #include #include #include #include #include #include "meta-wayland-data-device.h" #include "meta-wayland-data-device-private.h" #include "meta-wayland-seat.h" #include "meta-wayland-pointer.h" #include "meta-wayland-private.h" #include "meta-dnd-actor-private.h" #include "gtk-primary-selection-server-protocol.h" #define ROOTWINDOW_DROP_MIME "application/x-rootwindow-drop" #define ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \ WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \ WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) 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; }; typedef struct _MetaWaylandDataSourcePrivate { 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; } MetaWaylandDataSourcePrivate; typedef struct _MetaWaylandDataSourceWayland { MetaWaylandDataSource parent; struct wl_resource *resource; } MetaWaylandDataSourceWayland; typedef struct _MetaWaylandDataSourcePrimary { MetaWaylandDataSourceWayland parent; struct wl_resource *resource; } MetaWaylandDataSourcePrimary; G_DEFINE_TYPE_WITH_PRIVATE (MetaWaylandDataSource, meta_wayland_data_source, G_TYPE_OBJECT); G_DEFINE_TYPE (MetaWaylandDataSourceWayland, meta_wayland_data_source_wayland, META_TYPE_WAYLAND_DATA_SOURCE); G_DEFINE_TYPE (MetaWaylandDataSourcePrimary, meta_wayland_data_source_primary, META_TYPE_WAYLAND_DATA_SOURCE_WAYLAND); static MetaWaylandDataSource * meta_wayland_data_source_wayland_new (struct wl_resource *resource); static MetaWaylandDataSource * meta_wayland_data_source_primary_new (struct wl_resource *resource); static void drag_grab_data_source_destroyed (gpointer data, GObject *where_the_object_was); static void unbind_resource (struct wl_resource *resource) { wl_list_remove (wl_resource_get_link (resource)); } static gboolean meta_wayland_source_get_in_ask (MetaWaylandDataSource *source) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); return priv->in_ask; } static void meta_wayland_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; } 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; actions = meta_wayland_data_source_get_actions (source); 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); } static void 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_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; } } static 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; } static void meta_wayland_data_source_set_seat (MetaWaylandDataSource *source, MetaWaylandSeat *seat) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); priv->seat = seat; } static 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 (const MetaWaylandDataSource *source) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private ((MetaWaylandDataSource *)source); return &priv->mime_types; } static void meta_wayland_data_source_cancel (MetaWaylandDataSource *source) { META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->cancel (source); } uint32_t meta_wayland_data_source_get_actions (MetaWaylandDataSource *source) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); return priv->dnd_actions; } 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; } static 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; } static 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_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; } static void meta_wayland_data_source_set_user_action (MetaWaylandDataSource *source, enum wl_data_device_manager_dnd_action 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) data_offer_update_action (offer); } 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 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); if (offer->source) meta_wayland_data_source_send (offer->source, mime_type, fd); else close (fd); } static void default_destructor (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; data_offer_update_action (offer); } static const struct wl_data_offer_interface data_offer_interface = { data_offer_accept, data_offer_receive, default_destructor, data_offer_finish, data_offer_set_actions, }; static void primary_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); MetaWaylandDataSource *source = offer->source; MetaWaylandSeat *seat; if (!offer->source) { close (fd); return; } seat = meta_wayland_data_source_get_seat (source); if (wl_resource_get_client (offer->resource) != meta_wayland_keyboard_get_focus_client (seat->keyboard)) { close (fd); return; } meta_wayland_data_source_send (offer->source, mime_type, fd); } static const struct gtk_primary_selection_offer_interface primary_offer_interface = { primary_offer_receive, default_destructor, }; static 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); } 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 && seat->data_device.dnd_data_source == offer->source && wl_resource_get_version (offer->resource) < WL_DATA_OFFER_ACTION_SINCE_VERSION) meta_wayland_data_source_notify_finish (offer->source); else { 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); } 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); } static struct wl_resource * meta_wayland_data_source_send_offer (MetaWaylandDataSource *source, struct wl_resource *target) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); MetaWaylandDataOffer *offer = g_slice_new0 (MetaWaylandDataOffer); char **p; offer->source = source; g_object_add_weak_pointer (G_OBJECT (source), (gpointer *)&offer->source); 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); wl_data_device_send_data_offer (target, offer->resource); wl_array_for_each (p, &priv->mime_types) wl_data_offer_send_offer (offer->resource, *p); data_offer_update_action (offer); meta_wayland_data_source_set_current_offer (source, offer); return offer->resource; } static struct wl_resource * meta_wayland_data_source_send_primary_offer (MetaWaylandDataSource *source, struct wl_resource *target) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); MetaWaylandDataOffer *offer = g_slice_new0 (MetaWaylandDataOffer); char **p; offer->source = source; g_object_add_weak_pointer (G_OBJECT (source), (gpointer *)&offer->source); 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); gtk_primary_selection_device_send_data_offer (target, offer->resource); wl_array_for_each (p, &priv->mime_types) gtk_primary_selection_offer_send_offer (offer->resource, *p); meta_wayland_data_source_set_current_offer (source, offer); return offer->resource; } 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_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); MetaWaylandDataSourceWayland *source_wayland = META_WAYLAND_DATA_SOURCE_WAYLAND (source); if (priv->actions_set) { wl_resource_post_error (source_wayland->resource, WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, "cannot set actions more than once"); return; } if (dnd_actions & ~(ALL_ACTIONS)) { wl_resource_post_error (source_wayland->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 (source_wayland->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, default_destructor, data_source_set_actions }; 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 struct gtk_primary_selection_source_interface primary_source_interface = { primary_source_offer, default_destructor, }; struct _MetaWaylandDragGrab { MetaWaylandPointerGrab generic; MetaWaylandKeyboardGrab keyboard_grab; MetaWaylandSeat *seat; struct wl_client *drag_client; MetaWaylandSurface *drag_focus; 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 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; 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; actions = meta_wayland_data_source_get_actions (source); 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); 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 (source && data_device_resource) offer = meta_wayland_data_source_send_offer (source, data_device_resource); drag_grab->drag_focus = surface; 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_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); } meta_wayland_drag_grab_set_source (drag_grab, NULL); 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); } 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)) { /* Detach the data source from the grab, it's meant to live longer */ meta_wayland_drag_grab_set_source (drag_grab, NULL); meta_wayland_data_source_set_seat (source, NULL); meta_wayland_surface_drag_dest_drop (drag_grab->drag_focus); meta_wayland_data_source_notify_drop_performed (source); meta_wayland_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 { meta_wayland_data_source_cancel (source); meta_wayland_data_source_set_current_offer (source, NULL); meta_wayland_data_device_set_dnd_source (&seat->data_device, NULL); 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) { 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); 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; meta_wayland_data_device_set_dnd_source (&drag_grab->seat->data_device, 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; ClutterPoint pos, surface_pos; ClutterModifierType modifiers; 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); clutter_actor_transform_stage_point (CLUTTER_ACTOR (meta_surface_actor_get_texture (surface->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) { 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_grab->feedback_actor = meta_dnd_actor_new (CLUTTER_ACTOR (drag_grab->drag_origin->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, CLUTTER_ACTOR (drag_grab->drag_surface->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; 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; } 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_set_focus (seat->keyboard, NULL); 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; MetaWaylandSeat *seat = wl_container_of (data_device, seat, data_device); struct wl_resource *data_device_resource; struct wl_client *focus_client = NULL; data_device->selection_data_source = NULL; focus_client = meta_wayland_keyboard_get_focus_client (seat->keyboard); if (focus_client) { data_device_resource = wl_resource_find_for_client (&data_device->resource_list, focus_client); if (data_device_resource) wl_data_device_send_selection (data_device_resource, NULL); } wl_signal_emit (&data_device->selection_ownership_signal, NULL); } static void meta_wayland_source_send (MetaWaylandDataSource *source, const gchar *mime_type, gint fd) { MetaWaylandDataSourceWayland *source_wayland = META_WAYLAND_DATA_SOURCE_WAYLAND (source); wl_data_source_send_send (source_wayland->resource, mime_type, fd); close (fd); } static void meta_wayland_source_target (MetaWaylandDataSource *source, const gchar *mime_type) { MetaWaylandDataSourceWayland *source_wayland = META_WAYLAND_DATA_SOURCE_WAYLAND (source); wl_data_source_send_target (source_wayland->resource, mime_type); } static void meta_wayland_source_cancel (MetaWaylandDataSource *source) { MetaWaylandDataSourceWayland *source_wayland = META_WAYLAND_DATA_SOURCE_WAYLAND (source); wl_data_source_send_cancelled (source_wayland->resource); } static void meta_wayland_source_action (MetaWaylandDataSource *source, enum wl_data_device_manager_dnd_action action) { MetaWaylandDataSourceWayland *source_wayland = META_WAYLAND_DATA_SOURCE_WAYLAND (source); if (wl_resource_get_version (source_wayland->resource) >= WL_DATA_SOURCE_ACTION_SINCE_VERSION) wl_data_source_send_action (source_wayland->resource, action); } static void meta_wayland_source_drop_performed (MetaWaylandDataSource *source) { MetaWaylandDataSourceWayland *source_wayland = META_WAYLAND_DATA_SOURCE_WAYLAND (source); if (wl_resource_get_version (source_wayland->resource) >= WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) wl_data_source_send_dnd_drop_performed (source_wayland->resource); } static void meta_wayland_source_drag_finished (MetaWaylandDataSource *source) { MetaWaylandDataSourceWayland *source_wayland = META_WAYLAND_DATA_SOURCE_WAYLAND (source); enum wl_data_device_manager_dnd_action action; if (meta_wayland_source_get_in_ask (source)) { action = meta_wayland_data_source_get_current_action (source); meta_wayland_source_action (source, action); } if (wl_resource_get_version (source_wayland->resource) >= WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) wl_data_source_send_dnd_finished (source_wayland->resource); } static void meta_wayland_source_finalize (GObject *object) { G_OBJECT_CLASS (meta_wayland_data_source_parent_class)->finalize (object); } static void meta_wayland_data_source_wayland_init (MetaWaylandDataSourceWayland *source_wayland) { } static void meta_wayland_data_source_wayland_class_init (MetaWaylandDataSourceWaylandClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaWaylandDataSourceClass *data_source_class = META_WAYLAND_DATA_SOURCE_CLASS (klass); object_class->finalize = meta_wayland_source_finalize; data_source_class->send = meta_wayland_source_send; data_source_class->target = meta_wayland_source_target; data_source_class->cancel = meta_wayland_source_cancel; data_source_class->action = meta_wayland_source_action; data_source_class->drop_performed = meta_wayland_source_drop_performed; data_source_class->drag_finished = meta_wayland_source_drag_finished; } static void meta_wayland_data_source_primary_send (MetaWaylandDataSource *source, const gchar *mime_type, gint fd) { MetaWaylandDataSourcePrimary *source_primary; source_primary = META_WAYLAND_DATA_SOURCE_PRIMARY (source); gtk_primary_selection_source_send_send (source_primary->resource, mime_type, fd); close (fd); } static void meta_wayland_data_source_primary_cancel (MetaWaylandDataSource *source) { MetaWaylandDataSourcePrimary *source_primary; source_primary = META_WAYLAND_DATA_SOURCE_PRIMARY (source); gtk_primary_selection_source_send_cancelled (source_primary->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; } 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; } 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; } static void meta_wayland_drag_dest_focus_in (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface, MetaWaylandDataOffer *offer) { MetaWaylandDragGrab *grab = data_device->current_grab; struct wl_display *display; struct wl_client *client; 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); if (wl_resource_get_version (offer->resource) >= WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) { source_actions = meta_wayland_data_source_get_actions (offer->source); wl_data_offer_send_source_actions (offer->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, offer->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; } 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_remove_weak_pointer (G_OBJECT (data_device->dnd_data_source), (gpointer *)&data_device->dnd_data_source); data_device->dnd_data_source = source; if (source) g_object_add_weak_pointer (G_OBJECT (data_device->dnd_data_source), (gpointer *)&data_device->dnd_data_source); wl_signal_emit (&data_device->dnd_ownership_signal, source); } void meta_wayland_data_device_set_selection (MetaWaylandDataDevice *data_device, MetaWaylandDataSource *source, guint32 serial) { MetaWaylandSeat *seat = wl_container_of (data_device, seat, data_device); struct wl_resource *data_device_resource, *offer; struct wl_client *focus_client; if (data_device->selection_data_source && data_device->selection_serial - serial < UINT32_MAX / 2) return; if (data_device->selection_data_source) { meta_wayland_data_source_cancel (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; focus_client = meta_wayland_keyboard_get_focus_client (seat->keyboard); if (focus_client) { data_device_resource = wl_resource_find_for_client (&data_device->resource_list, focus_client); if (data_device_resource) { if (data_device->selection_data_source) { offer = meta_wayland_data_source_send_offer (data_device->selection_data_source, data_device_resource); wl_data_device_send_selection (data_device_resource, offer); } else { wl_data_device_send_selection (data_device_resource, NULL); } } } if (source) { meta_wayland_data_source_set_seat (source, seat); g_object_weak_ref (G_OBJECT (source), selection_data_source_destroyed, data_device); } wl_signal_emit (&data_device->selection_ownership_signal, 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); MetaWaylandDataSourcePrivate *priv; MetaWaylandDataSource *source; if (source_resource) source = wl_resource_get_user_data (source_resource); else source = NULL; if (source) { priv = meta_wayland_data_source_get_instance_private (source); if (priv->actions_set) { wl_resource_post_error(source_resource, WL_DATA_SOURCE_ERROR_INVALID_SOURCE, "cannot set drag-and-drop source as selection"); 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 primary_source_destroyed (gpointer data, GObject *object_was_here) { MetaWaylandDataDevice *data_device = data; MetaWaylandSeat *seat = wl_container_of (data_device, seat, data_device); struct wl_client *focus_client = NULL; data_device->primary_data_source = NULL; focus_client = meta_wayland_keyboard_get_focus_client (seat->keyboard); if (focus_client) { struct wl_resource *data_device_resource; data_device_resource = wl_resource_find_for_client (&data_device->primary_resource_list, focus_client); if (data_device_resource) gtk_primary_selection_device_send_selection (data_device_resource, NULL); } wl_signal_emit (&data_device->primary_ownership_signal, NULL); } void meta_wayland_data_device_set_primary (MetaWaylandDataDevice *data_device, MetaWaylandDataSource *source, guint32 serial) { MetaWaylandSeat *seat = wl_container_of (data_device, seat, data_device); struct wl_resource *data_device_resource, *offer; struct wl_client *focus_client; if (META_IS_WAYLAND_DATA_SOURCE_PRIMARY (source)) { struct wl_resource *resource; resource = META_WAYLAND_DATA_SOURCE_PRIMARY (source)->resource; if (wl_resource_get_client (resource) != meta_wayland_keyboard_get_focus_client (seat->keyboard)) return; } if (data_device->primary_data_source && data_device->primary_serial - serial < UINT32_MAX / 2) return; if (data_device->primary_data_source) { meta_wayland_data_source_cancel (data_device->primary_data_source); g_object_weak_unref (G_OBJECT (data_device->primary_data_source), primary_source_destroyed, data_device); } data_device->primary_data_source = source; data_device->primary_serial = serial; focus_client = meta_wayland_keyboard_get_focus_client (seat->keyboard); if (focus_client) { data_device_resource = wl_resource_find_for_client (&data_device->primary_resource_list, focus_client); if (data_device_resource) { if (data_device->primary_data_source) { offer = meta_wayland_data_source_send_primary_offer (data_device->primary_data_source, data_device_resource); gtk_primary_selection_device_send_selection (data_device_resource, offer); } else { gtk_primary_selection_device_send_selection (data_device_resource, NULL); } } } if (source) { meta_wayland_data_source_set_seat (source, seat); g_object_weak_ref (G_OBJECT (source), primary_source_destroyed, data_device); } wl_signal_emit (&data_device->primary_ownership_signal, source); } static void primary_device_set_selection (struct wl_client *client, struct wl_resource *resource, struct wl_resource *source_resource, uint32_t serial) { MetaWaylandDataDevice *data_device = wl_resource_get_user_data (resource); MetaWaylandDataSource *source; source = wl_resource_get_user_data (source_resource); meta_wayland_data_device_set_primary (data_device, source, serial); } static const struct gtk_primary_selection_device_interface primary_device_interface = { primary_device_set_selection, default_destructor, }; static void destroy_data_source (struct wl_resource *resource) { MetaWaylandDataSourceWayland *source = wl_resource_get_user_data (resource); source->resource = NULL; g_object_unref (source); } 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_wayland_new (source_resource); } 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)); } static const struct wl_data_device_manager_interface manager_interface = { create_data_source, get_data_device }; static void destroy_primary_source (struct wl_resource *resource) { MetaWaylandDataSourcePrimary *source = wl_resource_get_user_data (resource); source->resource = NULL; g_object_unref (source); } 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_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->data_device, unbind_resource); wl_list_insert (&seat->data_device.primary_resource_list, wl_resource_get_link (cr)); } 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_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); } 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_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"); 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_init (MetaWaylandDataDevice *data_device) { wl_list_init (&data_device->resource_list); wl_list_init (&data_device->primary_resource_list); wl_signal_init (&data_device->selection_ownership_signal); wl_signal_init (&data_device->primary_ownership_signal); wl_signal_init (&data_device->dnd_ownership_signal); } 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, *offer; MetaWaylandDataSource *source; focus_client = meta_wayland_keyboard_get_focus_client (seat->keyboard); if (focus_client == data_device->focus_client) return; data_device->focus_client = focus_client; if (!focus_client) return; data_device_resource = wl_resource_find_for_client (&data_device->resource_list, focus_client); if (data_device_resource) { source = data_device->selection_data_source; if (source) { offer = meta_wayland_data_source_send_offer (source, data_device_resource); wl_data_device_send_selection (data_device_resource, offer); } else { wl_data_device_send_selection (data_device_resource, NULL); } } data_device_resource = wl_resource_find_for_client (&data_device->primary_resource_list, focus_client); if (data_device_resource) { source = data_device->primary_data_source; if (source) { offer = meta_wayland_data_source_send_primary_offer (source, data_device_resource); gtk_primary_selection_device_send_selection (data_device_resource, offer); } else { gtk_primary_selection_device_send_selection (data_device_resource, NULL); } } } 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; } gboolean meta_wayland_data_source_has_mime_type (const MetaWaylandDataSource *source, const gchar *mime_type) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private ((MetaWaylandDataSource *)source); gchar **p; wl_array_for_each (p, &priv->mime_types) { if (g_strcmp0 (mime_type, *p) == 0) return TRUE; } return FALSE; } static MetaWaylandDataSource * meta_wayland_data_source_wayland_new (struct wl_resource *resource) { MetaWaylandDataSourceWayland *source_wayland = g_object_new (META_TYPE_WAYLAND_DATA_SOURCE_WAYLAND, NULL); source_wayland->resource = resource; wl_resource_set_implementation (resource, &data_source_interface, source_wayland, destroy_data_source); return META_WAYLAND_DATA_SOURCE (source_wayland); } static MetaWaylandDataSource * meta_wayland_data_source_primary_new (struct wl_resource *resource) { MetaWaylandDataSourcePrimary *source_primary = g_object_new (META_TYPE_WAYLAND_DATA_SOURCE_PRIMARY, NULL); source_primary->resource = resource; wl_resource_set_implementation (resource, &primary_source_interface, source_primary, destroy_primary_source); return META_WAYLAND_DATA_SOURCE (source_primary); } gboolean meta_wayland_data_source_add_mime_type (MetaWaylandDataSource *source, const gchar *mime_type) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); gchar **pos; pos = wl_array_add (&priv->mime_types, sizeof (*pos)); if (pos) { *pos = g_strdup (mime_type); return *pos != NULL; } return FALSE; } ukwm/src/wayland/meta-wayland-keyboard.c0000664000175000017500000007314713260055411017247 0ustar fengfeng/* * Wayland Support * * Copyright (C) 2013 Intel Corporation * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 #include "display-private.h" #include "backends/meta-backend-private.h" #include "meta-wayland-private.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #endif #define USD_KEYBOARD_SCHEMA "org.mate.peripherals-keyboard" typedef enum { GSD_KEYBOARD_NUM_LOCK_STATE_UNKNOWN, GSD_KEYBOARD_NUM_LOCK_STATE_ON, GSD_KEYBOARD_NUM_LOCK_STATE_OFF } GsdKeyboardNumLockState; G_DEFINE_TYPE (MetaWaylandKeyboard, meta_wayland_keyboard, META_TYPE_WAYLAND_INPUT_DEVICE) static void meta_wayland_keyboard_update_xkb_state (MetaWaylandKeyboard *keyboard); static void meta_wayland_keyboard_set_numlock (MetaWaylandKeyboard *keyboard, gboolean numlock_state); 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[] = "ukwm-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 inform_clients_of_new_keymap (MetaWaylandKeyboard *keyboard) { struct wl_resource *keyboard_resource; wl_resource_for_each (keyboard_resource, &keyboard->resource_list) { wl_keyboard_send_keymap (keyboard_resource, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keyboard->xkb_info.keymap_fd, keyboard->xkb_info.keymap_size); } wl_resource_for_each (keyboard_resource, &keyboard->focus_resource_list) { wl_keyboard_send_keymap (keyboard_resource, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keyboard->xkb_info.keymap_fd, keyboard->xkb_info.keymap_size); } } static void meta_wayland_keyboard_take_keymap (MetaWaylandKeyboard *keyboard, struct xkb_keymap *keymap) { MetaWaylandXkbInfo *xkb_info = &keyboard->xkb_info; GError *error = NULL; char *keymap_str; size_t previous_size; if (keymap == NULL) { g_warning ("Attempting to set null keymap (compilation probably failed)"); return; } xkb_keymap_unref (xkb_info->keymap); xkb_info->keymap = xkb_keymap_ref (keymap); meta_wayland_keyboard_update_xkb_state (keyboard); keymap_str = xkb_map_get_as_string (xkb_info->keymap); if (keymap_str == NULL) { g_warning ("failed to get string version of keymap"); return; } previous_size = xkb_info->keymap_size; xkb_info->keymap_size = strlen (keymap_str) + 1; if (xkb_info->keymap_fd >= 0) close (xkb_info->keymap_fd); xkb_info->keymap_fd = create_anonymous_file (xkb_info->keymap_size, &error); if (xkb_info->keymap_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); goto err_keymap_str; } if (xkb_info->keymap_area) munmap (xkb_info->keymap_area, previous_size); xkb_info->keymap_area = mmap (NULL, xkb_info->keymap_size, PROT_READ | PROT_WRITE, MAP_SHARED, xkb_info->keymap_fd, 0); if (xkb_info->keymap_area == MAP_FAILED) { g_warning ("failed to mmap() %lu bytes\n", (unsigned long) xkb_info->keymap_size); goto err_dev_zero; } strcpy (xkb_info->keymap_area, keymap_str); free (keymap_str); inform_clients_of_new_keymap (keyboard); notify_modifiers (keyboard); return; err_dev_zero: close (xkb_info->keymap_fd); xkb_info->keymap_fd = -1; err_keymap_str: free (keymap_str); return; } 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); 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); keyboard->key_serial = meta_wayland_input_device_next_serial (input_device); wl_resource_for_each (resource, &keyboard->focus_resource_list) { wl_keyboard_send_key (resource, keyboard->key_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 numlock_set_xkb_state (MetaWaylandKeyboard *keyboard, GsdKeyboardNumLockState state) { MetaBackend *backend = meta_get_backend (); gboolean numlock_state; if (state != GSD_KEYBOARD_NUM_LOCK_STATE_ON && state != GSD_KEYBOARD_NUM_LOCK_STATE_OFF) return; numlock_state = (state == GSD_KEYBOARD_NUM_LOCK_STATE_ON); meta_verbose ("set numlock state %s\n", (numlock_state ? "ON" : "OFF")); meta_backend_set_numlock (backend, numlock_state); meta_wayland_keyboard_set_numlock (keyboard, numlock_state); } static void maybe_restore_numlock_state (MetaWaylandKeyboard *keyboard) { gboolean remember_numlock; if (!keyboard->gsd_settings) return; /* We are cheating for now, we use g-s-d settings... */ remember_numlock = g_settings_get_boolean (keyboard->gsd_settings, "remember-numlock-state"); if (remember_numlock) { GsdKeyboardNumLockState state; state = g_settings_get_enum (keyboard->gsd_settings, "numlock-state"); numlock_set_xkb_state (keyboard, state); } } static void maybe_save_numlock_state (MetaWaylandKeyboard *keyboard) { MetaWaylandXkbInfo *xkb_info = &keyboard->xkb_info; GsdKeyboardNumLockState numlock_state; int numlock_active; if (!META_IS_BACKEND_NATIVE (meta_get_backend ())) return; if (!xkb_info->state) return; if (!keyboard->gsd_settings) return; if (!g_settings_get_boolean (keyboard->gsd_settings, "remember-numlock-state")) return; numlock_active = xkb_state_mod_name_is_active(xkb_info->state, "Mod2", XKB_STATE_MODS_LOCKED); switch (numlock_active) { case -1: numlock_state = GSD_KEYBOARD_NUM_LOCK_STATE_UNKNOWN; break; case 0: numlock_state = GSD_KEYBOARD_NUM_LOCK_STATE_OFF; break; default: numlock_state = GSD_KEYBOARD_NUM_LOCK_STATE_ON; break; } g_settings_set_enum (keyboard->gsd_settings, "numlock-state", numlock_state); } static void meta_wayland_keyboard_set_numlock (MetaWaylandKeyboard *keyboard, gboolean numlock_state) { MetaWaylandXkbInfo *xkb_info = &keyboard->xkb_info; xkb_mod_mask_t latched, locked, group, depressed; xkb_mod_mask_t numlock; meta_verbose ("backend numlock state %s\n", (numlock_state ? "ON" : "OFF")); latched = xkb_state_serialize_mods (xkb_info->state, XKB_STATE_MODS_LATCHED); locked = xkb_state_serialize_mods (xkb_info->state, XKB_STATE_MODS_LOCKED); group = xkb_state_serialize_layout (xkb_info->state, XKB_STATE_LAYOUT_EFFECTIVE); depressed = xkb_state_serialize_mods(xkb_info->state, XKB_STATE_DEPRESSED); numlock = (1 << xkb_keymap_mod_get_index(xkb_info->keymap, "Mod2")); if (numlock_state == TRUE) locked |= numlock; else locked &= ~numlock; xkb_state_update_mask (xkb_info->state, depressed, latched, locked, 0, 0, group); notify_modifiers (keyboard); } static void meta_wayland_keyboard_update_xkb_state (MetaWaylandKeyboard *keyboard) { MetaWaylandXkbInfo *xkb_info = &keyboard->xkb_info; xkb_mod_mask_t latched, locked; /* 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; } xkb_info->state = xkb_state_new (xkb_info->keymap); if (latched || locked) xkb_state_update_mask (xkb_info->state, 0, latched, locked, 0, 0, 0); } 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 remember_numlock_state_changed (GSettings *settings, const char *key, gpointer data) { MetaWaylandKeyboard *keyboard = data; maybe_save_numlock_state (keyboard); } 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; #ifdef HAVE_NATIVE_BACKEND MetaBackend *backend = meta_get_backend (); #endif /* Synthetic key events are for autorepeat. Ignore those, as * autorepeat in Wayland is done on the client side. */ if (event->key.flags & CLUTTER_EVENT_FLAG_SYNTHETIC) return FALSE; #ifdef HAVE_NATIVE_BACKEND if (META_IS_BACKEND_NATIVE (backend)) code = clutter_evdev_event_get_event_code (event); else #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 (); GSettingsSchema *schema; keyboard->settings = g_settings_new ("org.gnome.desktop.peripherals.keyboard"); g_signal_connect (keyboard->settings, "changed", G_CALLBACK (settings_changed), keyboard); /* We are cheating for now, we use g-s-d settings... Check if available */ schema = g_settings_schema_source_lookup (g_settings_schema_source_get_default (), USD_KEYBOARD_SCHEMA, TRUE); if (schema) { keyboard->gsd_settings = g_settings_new_full (schema, NULL, NULL); g_settings_schema_unref (schema); g_signal_connect (keyboard->gsd_settings, "changed::remember-numlock-state", G_CALLBACK (remember_numlock_state_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); meta_wayland_keyboard_take_keymap (keyboard, meta_backend_get_keymap (backend)); maybe_restore_numlock_state (keyboard); } static void meta_wayland_xkb_info_init (MetaWaylandXkbInfo *xkb_info) { xkb_info->keymap_fd = -1; } 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); if (xkb_info->keymap_area) { munmap (xkb_info->keymap_area, xkb_info->keymap_size); xkb_info->keymap_area = NULL; } if (xkb_info->keymap_fd >= 0) { close (xkb_info->keymap_fd); xkb_info->keymap_fd = -1; } } 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); meta_wayland_xkb_info_destroy (&keyboard->xkb_info); 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); if (keyboard->gsd_settings) g_object_unref (keyboard->gsd_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; /* 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); } gboolean meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard, const ClutterKeyEvent *event) { gboolean is_press = event->type == CLUTTER_KEY_PRESS; 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) 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) { if (keyboard->mods_changed & XKB_STATE_MODS_LOCKED) maybe_save_numlock_state (keyboard); 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); } 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)); 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); wl_keyboard_send_keymap (resource, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keyboard->xkb_info.keymap_fd, keyboard->xkb_info.keymap_size); 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_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); meta_wayland_xkb_info_init (&keyboard->xkb_info); 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_class_init (MetaWaylandKeyboardClass *klass) { } ukwm/src/wayland/meta-wayland-buffer.c0000664000175000017500000003443213233511035016711 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "meta-wayland-buffer.h" #include "meta-wayland-dma-buf.h" #include #include #include #include #ifndef DRM_FORMAT_MOD_INVALID #define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1) #endif #include "backends/meta-backend-private.h" 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; } static gboolean meta_wayland_buffer_is_realized (MetaWaylandBuffer *buffer) { return buffer->type != META_WAYLAND_BUFFER_TYPE_UNKNOWN; } static 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); MetaWaylandEglStream *stream; MetaWaylandDmaBufBuffer *dma_buf; if (wl_shm_buffer_get (buffer->resource) != NULL) { buffer->type = META_WAYLAND_BUFFER_TYPE_SHM; return TRUE; } 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; } stream = meta_wayland_egl_stream_new (buffer, NULL); if (stream) { buffer->egl_stream.stream = stream; buffer->type = META_WAYLAND_BUFFER_TYPE_EGL_STREAM; 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, 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 *texture; if (buffer->texture) return TRUE; 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); wl_shm_buffer_begin_access (shm_buffer); shm_buffer_get_cogl_pixel_format (shm_buffer, &format, &components); bitmap = cogl_bitmap_new_for_data (cogl_context, width, height, format, stride, wl_shm_buffer_get_data (shm_buffer)); texture = COGL_TEXTURE (cogl_texture_2d_new_from_bitmap (bitmap)); cogl_texture_set_components (COGL_TEXTURE (texture), components); cogl_object_unref (bitmap); if (!cogl_texture_allocate (COGL_TEXTURE (texture), error)) g_clear_pointer (&texture, cogl_object_unref); wl_shm_buffer_end_access (shm_buffer); buffer->texture = texture; buffer->is_y_inverted = TRUE; if (!buffer->texture) return FALSE; return TRUE; } static gboolean egl_image_buffer_attach (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); int format, width, height, y_inverted; CoglPixelFormat cogl_format; EGLImageKHR egl_image; CoglTexture2D *texture; if (buffer->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; texture = cogl_egl_texture_2d_new_from_image (cogl_context, width, height, cogl_format, egl_image, error); meta_egl_destroy_image (egl, egl_display, egl_image, NULL); if (!texture) return FALSE; buffer->texture = COGL_TEXTURE (texture); buffer->is_y_inverted = !!y_inverted; return TRUE; } static gboolean egl_stream_buffer_attach (MetaWaylandBuffer *buffer, GError **error) { MetaWaylandEglStream *stream = buffer->egl_stream.stream; g_assert (stream); if (!buffer->texture) { CoglTexture2D *texture; texture = meta_wayland_egl_stream_create_texture (stream, error); if (!texture) return FALSE; buffer->texture = COGL_TEXTURE (texture); buffer->is_y_inverted = meta_wayland_egl_stream_is_y_inverted (stream); } if (!meta_wayland_egl_stream_attach (stream, error)) return FALSE; return TRUE; } gboolean meta_wayland_buffer_attach (MetaWaylandBuffer *buffer, GError **error) { g_return_val_if_fail (buffer->resource, FALSE); if (!meta_wayland_buffer_is_realized (buffer)) { if (!meta_wayland_buffer_realize (buffer)) { 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, error); case META_WAYLAND_BUFFER_TYPE_EGL_IMAGE: return egl_image_buffer_attach (buffer, error); case META_WAYLAND_BUFFER_TYPE_EGL_STREAM: return egl_stream_buffer_attach (buffer, error); case META_WAYLAND_BUFFER_TYPE_DMA_BUF: return meta_wayland_dma_buf_buffer_attach (buffer, error); case META_WAYLAND_BUFFER_TYPE_UNKNOWN: g_assert_not_reached (); return FALSE; } g_assert_not_reached (); } CoglTexture * meta_wayland_buffer_get_texture (MetaWaylandBuffer *buffer) { return buffer->texture; } CoglSnippet * meta_wayland_buffer_create_snippet (MetaWaylandBuffer *buffer) { if (!buffer->egl_stream.stream) return NULL; return meta_wayland_egl_stream_create_snippet (); } gboolean meta_wayland_buffer_is_y_inverted (MetaWaylandBuffer *buffer) { return buffer->is_y_inverted; } static gboolean process_shm_buffer_damage (MetaWaylandBuffer *buffer, cairo_region_t *region, GError **error) { struct wl_shm_buffer *shm_buffer; int i, n_rectangles; gboolean set_texture_failed = FALSE; n_rectangles = cairo_region_num_rectangles (region); shm_buffer = wl_shm_buffer_get (buffer->resource); 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); CoglPixelFormat format; int bpp; cairo_rectangle_int_t rect; shm_buffer_get_cogl_pixel_format (shm_buffer, &format, NULL); bpp = _cogl_pixel_format_get_bytes_per_pixel (format); cairo_region_get_rectangle (region, i, &rect); if (!_cogl_texture_set_region (buffer->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, 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, region, &error); break; case META_WAYLAND_BUFFER_TYPE_EGL_IMAGE: case META_WAYLAND_BUFFER_TYPE_EGL_STREAM: 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->texture, cogl_object_unref); g_clear_object (&buffer->egl_stream.stream); 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; 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); } ukwm/src/wayland/meta-wayland-input-device.h0000664000175000017500000000311513233511035020033 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 */ ukwm/src/wayland/meta-wayland-surface.h0000664000175000017500000003371013233511035017073 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef META_WAYLAND_SURFACE_H #define META_WAYLAND_SURFACE_H #include #include #include #include #include #include #include "meta-wayland-types.h" #include "meta-surface-actor.h" #include "backends/meta-monitor-manager-private.h" #include "meta-wayland-pointer-constraints.h" typedef struct _MetaWaylandPendingState MetaWaylandPendingState; #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_PENDING_STATE (meta_wayland_pending_state_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandPendingState, meta_wayland_pending_state, META, WAYLAND_PENDING_STATE, GObject); struct _MetaWaylandSurfaceRoleClass { GObjectClass parent_class; void (*assigned) (MetaWaylandSurfaceRole *surface_role); void (*pre_commit) (MetaWaylandSurfaceRole *surface_role, MetaWaylandPendingState *pending); void (*commit) (MetaWaylandSurfaceRole *surface_role, MetaWaylandPendingState *pending); gboolean (*is_on_logical_monitor) (MetaWaylandSurfaceRole *surface_role, MetaLogicalMonitor *logical_monitor); MetaWaylandSurface * (*get_toplevel) (MetaWaylandSurfaceRole *surface_role); }; struct _MetaWaylandSerial { gboolean set; uint32_t value; }; #define META_TYPE_WAYLAND_SURFACE_ROLE_ACTOR_SURFACE (meta_wayland_surface_role_actor_surface_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaWaylandSurfaceRoleActorSurface, meta_wayland_surface_role_actor_surface, META, WAYLAND_SURFACE_ROLE_ACTOR_SURFACE, MetaWaylandSurfaceRole); struct _MetaWaylandSurfaceRoleActorSurfaceClass { MetaWaylandSurfaceRoleClass parent_class; }; #define META_TYPE_WAYLAND_SURFACE_ROLE_SHELL_SURFACE (meta_wayland_surface_role_shell_surface_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaWaylandSurfaceRoleShellSurface, meta_wayland_surface_role_shell_surface, META, WAYLAND_SURFACE_ROLE_SHELL_SURFACE, MetaWaylandSurfaceRoleActorSurface); struct _MetaWaylandSurfaceRoleShellSurfaceClass { MetaWaylandSurfaceRoleActorSurfaceClass parent_class; void (*configure) (MetaWaylandSurfaceRoleShellSurface *shell_surface_role, int new_x, int new_y, int new_width, int new_height, MetaWaylandSerial *sent_serial); void (*managed) (MetaWaylandSurfaceRoleShellSurface *shell_surface_role, MetaWindow *window); void (*ping) (MetaWaylandSurfaceRoleShellSurface *shell_surface_role, uint32_t serial); void (*close) (MetaWaylandSurfaceRoleShellSurface *shell_surface_role); }; #define META_TYPE_WAYLAND_SURFACE_ROLE_SUBSURFACE (meta_wayland_surface_role_subsurface_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandSurfaceRoleSubsurface, meta_wayland_surface_role_subsurface, META, WAYLAND_SURFACE_ROLE_SUBSURFACE, MetaWaylandSurfaceRoleActorSurface); #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, MetaWaylandSurfaceRole); struct _MetaWaylandPendingState { 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; /* 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; }; 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; MetaSurfaceActor *surface_actor; MetaWaylandSurfaceRole *role; MetaWindow *window; cairo_region_t *input_region; cairo_region_t *opaque_region; int scale; int32_t offset_x, offset_y; GList *subsurfaces; GHashTable *outputs_to_destroy_notify_id; /* Buffer reference state. */ struct { MetaWaylandBuffer *buffer; unsigned int use_count; } buffer_ref; /* Buffer renderer state. */ gboolean buffer_held; /* List of pending frame callbacks that needs to stay queued longer than one * commit sequence, such as when it has not yet been assigned a role. */ struct wl_list pending_frame_callback_list; /* Intermediate state for when no role has been assigned. */ struct { MetaWaylandBuffer *buffer; } unassigned; struct { const MetaWaylandDragDestFuncs *funcs; } dnd; /* All the pending state that wl_surface.commit will apply. */ MetaWaylandPendingState *pending; /* 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; MetaWaylandPendingState *pending; int32_t pending_x; int32_t pending_y; gboolean pending_pos; GSList *pending_placement_ops; } sub; /* 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); 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, int new_x, int new_y, int width, int height, MetaWaylandSerial *sent_serial); 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_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, MetaWaylandPendingState *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); void meta_wayland_surface_calculate_window_geometry (MetaWaylandSurface *surface, MetaRectangle *total_geometry, float parent_x, float parent_y); void meta_wayland_surface_destroy_window (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); #endif ukwm/src/wayland/meta-wayland-tablet-pad-ring.h0000664000175000017500000000431013220600404020402 0ustar fengfeng/* * 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 "meta-wayland-types.h" #include "meta-cursor-renderer.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 */ ukwm/src/wayland/meta-wayland-surface-role-tablet-cursor.c0000664000175000017500000000246713233511035022616 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "config.h" #include "meta-wayland-surface-role-tablet-cursor.h" struct _MetaWaylandSurfaceRoleTabletCursor { MetaWaylandSurfaceRoleCursor parent; }; G_DEFINE_TYPE (MetaWaylandSurfaceRoleTabletCursor, meta_wayland_surface_role_tablet_cursor, META_TYPE_WAYLAND_SURFACE_ROLE_CURSOR) static void meta_wayland_surface_role_tablet_cursor_init (MetaWaylandSurfaceRoleTabletCursor *role) { } static void meta_wayland_surface_role_tablet_cursor_class_init (MetaWaylandSurfaceRoleTabletCursorClass *klass) { } ukwm/src/wayland/meta-wayland-tablet-seat.c0000664000175000017500000004101413233511035017637 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author: Carlos Garnacho */ #define _GNU_SOURCE #include "config.h" #include #include #include "tablet-unstable-v2-server-protocol.h" #include "meta-wayland-private.h" #include "meta-wayland-tablet-seat.h" #include "meta-wayland-tablet.h" #include "meta-wayland-tablet-tool.h" #include "meta-wayland-tablet-pad.h" #ifdef HAVE_NATIVE_BACKEND #include #include "backends/native/meta-backend-native.h" #endif 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; const GSList *devices, *l; tablet_seat = g_slice_new0 (MetaWaylandTabletSeat); tablet_seat->manager = manager; tablet_seat->seat = seat; tablet_seat->device_manager = clutter_device_manager_get_default (); 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->device_manager, "device-added", G_CALLBACK (meta_wayland_tablet_seat_device_added), tablet_seat); g_signal_connect_swapped (tablet_seat->device_manager, "device-removed", G_CALLBACK (meta_wayland_tablet_seat_device_removed), tablet_seat); devices = clutter_device_manager_peek_devices (tablet_seat->device_manager); for (l = devices; l; l = l->next) meta_wayland_tablet_seat_device_added (tablet_seat, l->data); 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->device_manager, 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) { ClutterDeviceManager *device_manager; const GSList *devices, *l; GList *group = NULL; device_manager = clutter_device_manager_get_default (); devices = clutter_device_manager_peek_devices (device_manager); 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); } 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); } ukwm/src/wayland/meta-wayland-inhibit-shortcuts-dialog.c0000664000175000017500000001372613220600404022355 0ustar fengfeng/* * 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 #include "wayland/meta-window-wayland.h" #include "wayland/meta-wayland.h" #include "core/window-private.h" #include "compositor/compositor-private.h" #include "meta-wayland-inhibit-shortcuts-dialog.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_signal_handler_disconnect (data->dialog, data->response_handler_id); 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"); } ukwm/src/wayland/meta-wayland-egl-stream.c0000664000175000017500000002130013233511035017466 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #include "config.h" #include "wayland/meta-wayland-egl-stream.h" #include "cogl/cogl-egl.h" #include "backends/meta-backend-private.h" #include "backends/meta-egl.h" #include "backends/meta-egl-ext.h" #include "meta/meta-backend.h" #include "wayland/meta-wayland-buffer.h" struct _MetaWaylandEglStream { GObject parent; EGLStreamKHR egl_stream; MetaWaylandBuffer *buffer; CoglTexture2D *texture; gboolean is_y_inverted; }; 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 (void) { 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);"); return 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); 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; } ukwm/src/wayland/meta-wayland-surface-role-cursor.h0000664000175000017500000000473413233511035021351 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef META_WAYLAND_SURFACE_ROLE_CURSOR_H #define META_WAYLAND_SURFACE_ROLE_CURSOR_H #include "meta-wayland-surface.h" #include "backends/meta-cursor-renderer.h" struct _MetaWaylandSurfaceRoleCursorClass { MetaWaylandSurfaceRoleClass parent_class; }; #define META_TYPE_WAYLAND_SURFACE_ROLE_CURSOR (meta_wayland_surface_role_cursor_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaWaylandSurfaceRoleCursor, meta_wayland_surface_role_cursor, META, WAYLAND_SURFACE_ROLE_CURSOR, MetaWaylandSurfaceRole); MetaCursorSprite * meta_wayland_surface_role_cursor_get_sprite (MetaWaylandSurfaceRoleCursor *cursor_role); void meta_wayland_surface_role_cursor_set_hotspot (MetaWaylandSurfaceRoleCursor *cursor_role, gint hotspot_x, gint hotspot_y); void meta_wayland_surface_role_cursor_get_hotspot (MetaWaylandSurfaceRoleCursor *cursor_role, gint *hotspot_x, gint *hotspot_y); void meta_wayland_surface_role_cursor_set_renderer (MetaWaylandSurfaceRoleCursor *cursor_role, MetaCursorRenderer *renderer); MetaCursorRenderer * meta_wayland_surface_role_cursor_get_renderer (MetaWaylandSurfaceRoleCursor *cursor_role); #endif /* META_WAYLAND_SURFACE_ROLE_CURSOR_H */ ukwm/src/wayland/meta-wayland-pointer-gesture-swipe.h0000664000175000017500000000304013220600404021710 0ustar fengfeng/* * 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 #include "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 */ ukwm/src/wayland/meta-wayland-tablet-pad-strip.c0000664000175000017500000001400613233511035020607 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author: Carlos Garnacho */ #define _GNU_SOURCE #include "config.h" #include #include #include "tablet-unstable-v2-server-protocol.h" #include "meta-surface-actor-wayland.h" #include "meta-wayland-private.h" #include "meta-wayland-tablet-pad.h" #include "meta-wayland-tablet-pad-group.h" #include "meta-wayland-tablet-pad-strip.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); } ukwm/src/wayland/meta-pointer-confinement-wayland.c0000664000175000017500000005603313233511035021424 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #include "config.h" #include "wayland/meta-pointer-confinement-wayland.h" #include #include #include "backends/meta-backend-private.h" #include "core/meta-border.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-pointer.h" #include "wayland/meta-wayland-pointer-constraints.h" #include "wayland/meta-wayland-surface.h" #include "backends/meta-pointer-constraint.h" #include "compositor/meta-surface-actor-wayland.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; ClutterPoint 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; 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); meta_backend_warp_pointer (meta_get_backend (), (int)x, (int)y); } cairo_region_destroy (region); } static void surface_actor_allocation_notify (MetaSurfaceActorWayland *surface_actor, GParamSpec *pspec, MetaPointerConfinementWayland *self) { meta_pointer_confinement_wayland_maybe_warp (self); } static void surface_actor_position_notify (MetaSurfaceActorWayland *surface_actor, GParamSpec *pspec, 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; 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->surface_actor, "notify::allocation", G_CALLBACK (surface_actor_allocation_notify), confinement, 0); g_signal_connect_object (surface->surface_actor, "notify::position", G_CALLBACK (surface_actor_position_notify), confinement, 0); if (surface->window) { g_signal_connect_object (surface->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; } ukwm/src/wayland/meta-wayland-touch.h0000664000175000017500000000574513220600404016567 0ustar fengfeng/* * 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 "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 */ ClutterInputDevice *device; 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 */ ukwm/src/wayland/meta-wayland-tablet-manager.c0000664000175000017500000002070613233511035020322 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author: Carlos Garnacho */ #define _GNU_SOURCE #include "config.h" #include #include #include "tablet-unstable-v2-server-protocol.h" #include "meta-wayland-private.h" #include "meta-wayland-tablet-manager.h" #include "meta-wayland-tablet-seat.h" #include "meta-wayland-tablet-tool.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) { ClutterDeviceManager *device_manager; device_manager = clutter_device_manager_get_default (); g_signal_handlers_disconnect_by_data (device_manager, 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); } } ukwm/src/wayland/meta-wayland-pointer-gestures.h0000664000175000017500000000204713220600404020754 0ustar fengfeng/* * 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 "meta-wayland-types.h" void meta_wayland_pointer_gestures_init (MetaWaylandCompositor *compositor); #endif /* META_WAYLAND_POINTER_GESTURES_H */ ukwm/src/wayland/meta-wayland-tablet-pad-ring.c0000664000175000017500000001367313233511035020416 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author: Carlos Garnacho */ #define _GNU_SOURCE #include "config.h" #include #include #include "tablet-unstable-v2-server-protocol.h" #include "meta-surface-actor-wayland.h" #include "meta-wayland-private.h" #include "meta-wayland-tablet-pad.h" #include "meta-wayland-tablet-pad-group.h" #include "meta-wayland-tablet-pad-ring.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); } ukwm/src/wayland/meta-wayland-inhibit-shortcuts-dialog.h0000664000175000017500000000230013220600404022344 0ustar fengfeng/* * 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 "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 */ ukwm/src/org.ukui.ukwm.DisplayConfig.xml0000664000175000017500000004156613220600404017256 0ustar fengfeng ukwm/src/meta/0000775000175000017500000000000013254604522012203 5ustar fengfengukwm/src/meta/screen.h0000664000175000017500000001146413220600404013625 0ustar fengfeng/* -*- 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_SCREEN_H #define META_SCREEN_H #include #include #include #include #define META_TYPE_SCREEN (meta_screen_get_type ()) #define META_SCREEN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_SCREEN, MetaScreen)) #define META_SCREEN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_SCREEN, MetaScreenClass)) #define META_IS_SCREEN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_SCREEN)) #define META_IS_SCREEN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_SCREEN)) #define META_SCREEN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_SCREEN, MetaScreenClass)) typedef struct _MetaScreenClass MetaScreenClass; GType meta_screen_get_type (void); int meta_screen_get_screen_number (MetaScreen *screen); MetaDisplay *meta_screen_get_display (MetaScreen *screen); Window meta_screen_get_xroot (MetaScreen *screen); void meta_screen_get_size (MetaScreen *screen, int *width, int *height); void meta_screen_set_cm_selection (MetaScreen *screen); GSList *meta_screen_get_startup_sequences (MetaScreen *screen); GList *meta_screen_get_workspaces (MetaScreen *screen); int meta_screen_get_n_workspaces (MetaScreen *screen); MetaWorkspace* meta_screen_get_workspace_by_index (MetaScreen *screen, int index); void meta_screen_remove_workspace (MetaScreen *screen, MetaWorkspace *workspace, guint32 timestamp); MetaWorkspace *meta_screen_append_new_workspace (MetaScreen *screen, gboolean activate, guint32 timestamp); int meta_screen_get_active_workspace_index (MetaScreen *screen); MetaWorkspace * meta_screen_get_active_workspace (MetaScreen *screen); /** * MetaScreenDirection: * @META_SCREEN_UP: up * @META_SCREEN_DOWN: down * @META_SCREEN_LEFT: left * @META_SCREEN_RIGHT: right */ typedef enum { META_SCREEN_UP, META_SCREEN_DOWN, META_SCREEN_LEFT, META_SCREEN_RIGHT } MetaScreenDirection; int meta_screen_get_n_monitors (MetaScreen *screen); int meta_screen_get_primary_monitor (MetaScreen *screen); int meta_screen_get_current_monitor (MetaScreen *screen); void meta_screen_get_monitor_geometry (MetaScreen *screen, int monitor, MetaRectangle *geometry); gboolean meta_screen_get_monitor_in_fullscreen (MetaScreen *screen, int monitor); int meta_screen_get_monitor_index_for_rect (MetaScreen *screen, MetaRectangle *rect); int meta_screen_get_monitor_neighbor_index (MetaScreen *screen, int which_monitor, MetaScreenDirection dir); void meta_screen_focus_default_window (MetaScreen *screen, guint32 timestamp); /** * MetaScreenCorner: * @META_SCREEN_TOPLEFT: top-left corner * @META_SCREEN_TOPRIGHT: top-right corner * @META_SCREEN_BOTTOMLEFT: bottom-left corner * @META_SCREEN_BOTTOMRIGHT: bottom-right corner */ typedef enum { META_SCREEN_TOPLEFT, META_SCREEN_TOPRIGHT, META_SCREEN_BOTTOMLEFT, META_SCREEN_BOTTOMRIGHT } MetaScreenCorner; void meta_screen_override_workspace_layout (MetaScreen *screen, MetaScreenCorner starting_corner, gboolean vertical_layout, int n_rows, int n_columns); void meta_screen_set_cursor (MetaScreen *screen, MetaCursor cursor); #endif ukwm/src/meta/compositor-ukwm.h0000664000175000017500000000414313220600404015521 0ustar fengfeng/* -*- 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 * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef UKWM_H_ #define UKWM_H_ #include #include #include #include #include #include /* Public compositor API */ ClutterActor *meta_get_stage_for_screen (MetaScreen *screen); Window meta_get_overlay_window (MetaScreen *screen); GList *meta_get_window_actors (MetaScreen *screen); ClutterActor *meta_get_window_group_for_screen (MetaScreen *screen); ClutterActor *meta_get_top_window_group_for_screen (MetaScreen *screen); ClutterActor *meta_get_feedback_group_for_screen (MetaScreen *screen); void meta_disable_unredirect_for_screen (MetaScreen *screen); void meta_enable_unredirect_for_screen (MetaScreen *screen); void meta_set_stage_input_region (MetaScreen *screen, XserverRegion region); void meta_empty_stage_input_region (MetaScreen *screen); void meta_focus_stage_window (MetaScreen *screen, guint32 timestamp); gboolean meta_stage_is_focused (MetaScreen *screen); #endif ukwm/src/meta/theme.h0000664000175000017500000000204413220600404013442 0ustar fengfeng/* -*- 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; MetaTheme* meta_theme_get_default (void); MetaTheme* meta_theme_new (void); void meta_theme_free (MetaTheme *theme); #endif ukwm/src/meta/meta-window-shape.h0000664000175000017500000000507713220600404015702 0ustar fengfeng/* -*- 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 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; MetaWindowShape * meta_window_shape_new (cairo_region_t *region); MetaWindowShape * meta_window_shape_ref (MetaWindowShape *shape); void meta_window_shape_unref (MetaWindowShape *shape); guint meta_window_shape_hash (MetaWindowShape *shape); gboolean meta_window_shape_equal (MetaWindowShape *shape_a, MetaWindowShape *shape_b); void meta_window_shape_get_borders (MetaWindowShape *shape, int *border_top, int *border_right, int *border_bottom, int *border_left); cairo_region_t *meta_window_shape_to_region (MetaWindowShape *shape, int center_width, int center_height); #endif /* __META_WINDOW_SHAPE_H __*/ ukwm/src/meta/boxes.h0000664000175000017500000001124713220600404013465 0ustar fengfeng/* -*- 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; }; GType meta_rectangle_get_type (void); MetaRectangle *meta_rectangle_copy (const MetaRectangle *rect); void meta_rectangle_free (MetaRectangle *rect); /* Function to make initializing a rect with a single line of code easy */ MetaRectangle meta_rect (int x, int y, int width, int height); /* Basic comparison functions */ int meta_rectangle_area (const MetaRectangle *rect); gboolean meta_rectangle_intersect (const MetaRectangle *src1, const MetaRectangle *src2, MetaRectangle *dest); gboolean meta_rectangle_equal (const MetaRectangle *src1, const MetaRectangle *src2); /* Find the bounding box of the union of two rectangles */ 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. */ 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. */ gboolean meta_rectangle_vert_overlap (const MetaRectangle *rect1, const MetaRectangle *rect2); 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. */ gboolean meta_rectangle_could_fit_rect (const MetaRectangle *outer_rect, const MetaRectangle *inner_rect); gboolean meta_rectangle_contains_rect (const MetaRectangle *outer_rect, const MetaRectangle *inner_rect); #endif /* META_BOXES_H */ ukwm/src/meta/workspace.h0000664000175000017500000000530013220600404014334 0ustar fengfeng/* -*- 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 #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; GType meta_workspace_get_type (void); int meta_workspace_index (MetaWorkspace *workspace); MetaScreen *meta_workspace_get_screen (MetaWorkspace *workspace); GList* meta_workspace_list_windows (MetaWorkspace *workspace); void meta_workspace_get_work_area_for_monitor (MetaWorkspace *workspace, int which_monitor, MetaRectangle *area); void meta_workspace_get_work_area_all_monitors (MetaWorkspace *workspace, MetaRectangle *area); void meta_workspace_activate (MetaWorkspace *workspace, guint32 timestamp); void meta_workspace_activate_with_focus (MetaWorkspace *workspace, MetaWindow *focus_this, guint32 timestamp); void meta_workspace_set_builtin_struts (MetaWorkspace *workspace, GSList *struts); MetaWorkspace* meta_workspace_get_neighbor (MetaWorkspace *workspace, MetaMotionDirection direction); #endif ukwm/src/meta/compositor.h0000664000175000017500000001410213220600404014534 0ustar fengfeng/* -*- 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 #include /** * 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, } MetaSizeChange; MetaCompositor *meta_compositor_new (MetaDisplay *display); void meta_compositor_destroy (MetaCompositor *compositor); void meta_compositor_manage (MetaCompositor *compositor); void meta_compositor_unmanage (MetaCompositor *compositor); void meta_compositor_window_shape_changed (MetaCompositor *compositor, MetaWindow *window); void meta_compositor_window_opacity_changed (MetaCompositor *compositor, MetaWindow *window); void meta_compositor_window_surface_changed (MetaCompositor *compositor, MetaWindow *window); gboolean meta_compositor_process_event (MetaCompositor *compositor, XEvent *event, MetaWindow *window); gboolean meta_compositor_filter_keybinding (MetaCompositor *compositor, MetaKeyBinding *binding); void meta_compositor_add_window (MetaCompositor *compositor, MetaWindow *window); void meta_compositor_remove_window (MetaCompositor *compositor, MetaWindow *window); void meta_compositor_show_window (MetaCompositor *compositor, MetaWindow *window, MetaCompEffect effect); void meta_compositor_hide_window (MetaCompositor *compositor, MetaWindow *window, MetaCompEffect effect); void meta_compositor_switch_workspace (MetaCompositor *compositor, MetaWorkspace *from, MetaWorkspace *to, MetaMotionDirection direction); void meta_compositor_size_change_window (MetaCompositor *compositor, MetaWindow *window, MetaSizeChange which_change, MetaRectangle *old_frame_rect, MetaRectangle *old_buffer_rect); void meta_compositor_sync_window_geometry (MetaCompositor *compositor, MetaWindow *window, gboolean did_placement); void meta_compositor_sync_updates_frozen (MetaCompositor *compositor, MetaWindow *window); void meta_compositor_queue_frame_drawn (MetaCompositor *compositor, MetaWindow *window, gboolean no_delay_frame); void meta_compositor_sync_stack (MetaCompositor *compositor, GList *stack); void meta_compositor_flash_screen (MetaCompositor *compositor, MetaScreen *screen); void meta_compositor_show_tile_preview (MetaCompositor *compositor, MetaWindow *window, MetaRectangle *tile_rect, int tile_monitor_number); void meta_compositor_hide_tile_preview (MetaCompositor *compositor); void meta_compositor_show_window_menu (MetaCompositor *compositor, MetaWindow *window, MetaWindowMenuType menu, int x, int y); void meta_compositor_show_window_menu_for_rect (MetaCompositor *compositor, MetaWindow *window, MetaWindowMenuType menu, MetaRectangle *rect); #endif /* META_COMPOSITOR_H */ ukwm/src/meta/meta-background-group.h0000664000175000017500000000260413220600404016537 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef META_BACKGROUND_GROUP_H #define META_BACKGROUND_GROUP_H #include #define META_TYPE_BACKGROUND_GROUP (meta_background_group_get_type ()) #define META_BACKGROUND_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BACKGROUND_GROUP, MetaBackgroundGroup)) #define META_BACKGROUND_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BACKGROUND_GROUP, MetaBackgroundGroupClass)) #define META_IS_BACKGROUND_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BACKGROUND_GROUP)) #define META_IS_BACKGROUND_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BACKGROUND_GROUP)) #define META_BACKGROUND_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BACKGROUND_GROUP, MetaBackgroundGroupClass)) typedef struct _MetaBackgroundGroup MetaBackgroundGroup; typedef struct _MetaBackgroundGroupClass MetaBackgroundGroupClass; typedef struct _MetaBackgroundGroupPrivate MetaBackgroundGroupPrivate; struct _MetaBackgroundGroupClass { /*< private >*/ ClutterActorClass parent_class; }; struct _MetaBackgroundGroup { /*< private >*/ ClutterActor parent; MetaBackgroundGroupPrivate *priv; }; GType meta_background_group_get_type (void); ClutterActor *meta_background_group_new (void); #endif /* META_BACKGROUND_GROUP_H */ ukwm/src/meta/errors.h0000664000175000017500000000227413220600404013661 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm X error handling */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_ERRORS_H #define META_ERRORS_H #include #include #include void meta_error_trap_push (MetaDisplay *display); void meta_error_trap_pop (MetaDisplay *display); /* returns X error code, or 0 for no error */ int meta_error_trap_pop_with_return (MetaDisplay *display); #endif ukwm/src/meta/meta-shadow-factory.h0000664000175000017500000001242113220600404016216 0ustar fengfeng/* -*- 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 #include 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 ()) #define META_SHADOW_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_SHADOW_FACTORY, MetaShadowFactory)) #define META_SHADOW_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_SHADOW_FACTORY, MetaShadowFactoryClass)) #define META_IS_SHADOW_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_SHADOW_FACTORY)) #define META_IS_SHADOW_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_SHADOW_FACTORY)) #define META_SHADOW_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_SHADOW_FACTORY, MetaShadowFactoryClass)) /** * 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. */ typedef struct _MetaShadowFactory MetaShadowFactory; typedef struct _MetaShadowFactoryClass MetaShadowFactoryClass; MetaShadowFactory *meta_shadow_factory_get_default (void); GType meta_shadow_factory_get_type (void); void meta_shadow_factory_set_params (MetaShadowFactory *factory, const char *class_name, gboolean focused, MetaShadowParams *params); 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; MetaShadow *meta_shadow_ref (MetaShadow *shadow); void meta_shadow_unref (MetaShadow *shadow); void meta_shadow_paint (MetaShadow *shadow, int window_x, int window_y, int window_width, int window_height, guint8 opacity, cairo_region_t *clip, gboolean clip_strictly); void meta_shadow_get_bounds (MetaShadow *shadow, int window_x, int window_y, int window_width, int window_height, cairo_rectangle_int_t *bounds); MetaShadowFactory *meta_shadow_factory_new (void); 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__ */ ukwm/src/meta/prefs.h0000664000175000017500000004227613220600404013472 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm preferences */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2006 Elijah Newren * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, 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_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_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_CENTER_NEW_WINDOWS: center new windows * @META_PREF_DRAG_THRESHOLD: drag threshold */ /* Keep in sync with GSettings schemas! */ typedef enum { META_PREF_MOUSE_BUTTON_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_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_CENTER_NEW_WINDOWS, META_PREF_DRAG_THRESHOLD, META_PREF_THEME, /* For UKUI theme control */ } MetaPreference; typedef void (* MetaPrefsChangedFunc) (MetaPreference pref, gpointer user_data); void meta_prefs_add_listener (MetaPrefsChangedFunc func, gpointer user_data); void meta_prefs_remove_listener (MetaPrefsChangedFunc func, gpointer user_data); void meta_prefs_init (void); void meta_prefs_override_preference_schema (const char *key, const char *schema); const char* meta_preference_to_string (MetaPreference pref); MetaVirtualModifier meta_prefs_get_mouse_button_mods (void); gint meta_prefs_get_mouse_button_resize (void); gint meta_prefs_get_mouse_button_menu (void); GDesktopFocusMode meta_prefs_get_focus_mode (void); GDesktopFocusNewWindows meta_prefs_get_focus_new_windows (void); gboolean meta_prefs_get_attach_modal_dialogs (void); gboolean meta_prefs_get_raise_on_click (void); /* For UKUI theme control */ const char* meta_prefs_get_theme (void); /* returns NULL if GTK default should be used */ const PangoFontDescription* meta_prefs_get_titlebar_font (void); int meta_prefs_get_num_workspaces (void); gboolean meta_prefs_get_dynamic_workspaces (void); gboolean meta_prefs_get_disable_workarounds (void); gboolean meta_prefs_get_auto_raise (void); int meta_prefs_get_auto_raise_delay (void); gboolean meta_prefs_get_focus_change_on_pointer_rest (void); gboolean meta_prefs_get_gnome_accessibility (void); gboolean meta_prefs_get_gnome_animations (void); gboolean meta_prefs_get_edge_tiling (void); gboolean meta_prefs_get_auto_maximize (void); gboolean meta_prefs_get_center_new_windows (void); gboolean meta_prefs_get_show_fallback_app_menu (void); void meta_prefs_get_button_layout (MetaButtonLayout *button_layout); /* Double, right, middle click can be configured to any titlebar meta-action */ GDesktopTitlebarAction meta_prefs_get_action_double_click_titlebar (void); GDesktopTitlebarAction meta_prefs_get_action_middle_click_titlebar (void); GDesktopTitlebarAction meta_prefs_get_action_right_click_titlebar (void); void meta_prefs_set_num_workspaces (int n_workspaces); const char* meta_prefs_get_workspace_name (int i); void meta_prefs_change_workspace_name (int i, const char *name); const char* meta_prefs_get_cursor_theme (void); int meta_prefs_get_cursor_size (void); gboolean meta_prefs_get_compositing_manager (void); gboolean meta_prefs_get_force_fullscreen (void); void meta_prefs_set_force_fullscreen (gboolean whether); gboolean meta_prefs_get_workspaces_only_on_primary (void); int meta_prefs_get_draggable_border_width (void); int meta_prefs_get_drag_threshold (void); gboolean meta_prefs_get_ignore_request_hide_titlebar (void); void meta_prefs_set_ignore_request_hide_titlebar (gboolean whether); /** * 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_TOGGLE_TILED_LEFT: FILLME * @META_KEYBINDING_ACTION_TOGGLE_TILED_RIGHT: 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_OVERLAY_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_TOGGLE_TILED_LEFT, META_KEYBINDING_ACTION_TOGGLE_TILED_RIGHT, 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_OVERLAY_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 */ 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, } MetaKeyBindingFlags; /** * MetaKeyHandlerFunc: * @display: a #MetaDisplay * @screen: a #MetaScreen * @window: a #MetaWindow * @event: (type gpointer): a #ClutterKeyEvent * @binding: a #MetaKeyBinding * @user_data: data passed to the function * */ typedef void (* MetaKeyHandlerFunc) (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer user_data); GType meta_key_binding_get_type (void); MetaKeyBindingAction meta_prefs_get_keybinding_action (const char *name); gboolean meta_prefs_get_visual_bell (void); gboolean meta_prefs_bell_is_audible (void); GDesktopVisualBellType meta_prefs_get_visual_bell_type (void); #endif ukwm/src/meta/types.h0000664000175000017500000000262213220600404013506 0ustar fengfeng/* -*- 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 _MetaFrame MetaFrame; typedef struct _MetaScreen MetaScreen; 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; #endif ukwm/src/meta/display.h0000664000175000017500000002135213220600404014010 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2008 Iain Holmes * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_DISPLAY_H #define META_DISPLAY_H #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 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)) GType meta_display_get_type (void) G_GNUC_CONST; #define meta_XFree(p) do { if ((p)) XFree ((p)); } while (0) int meta_display_get_xinput_opcode (MetaDisplay *display); gboolean meta_display_supports_extended_barriers (MetaDisplay *display); Display *meta_display_get_xdisplay (MetaDisplay *display); MetaCompositor *meta_display_get_compositor (MetaDisplay *display); gboolean meta_display_has_shape (MetaDisplay *display); MetaWindow *meta_display_get_focus_window (MetaDisplay *display); gboolean meta_display_xwindow_is_a_no_focus_window (MetaDisplay *display, Window xwindow); int meta_display_get_damage_event_base (MetaDisplay *display); int meta_display_get_shape_event_base (MetaDisplay *display); gboolean meta_display_xserver_time_is_before (MetaDisplay *display, guint32 time1, guint32 time2); guint32 meta_display_get_last_user_time (MetaDisplay *display); guint32 meta_display_get_current_time (MetaDisplay *display); guint32 meta_display_get_current_time_roundtrip (MetaDisplay *display); GList* meta_display_get_tab_list (MetaDisplay *display, MetaTabList type, MetaWorkspace *workspace); MetaWindow* meta_display_get_tab_next (MetaDisplay *display, MetaTabList type, MetaWorkspace *workspace, MetaWindow *window, gboolean backward); MetaWindow* meta_display_get_tab_current (MetaDisplay *display, MetaTabList type, MetaWorkspace *workspace); gboolean meta_display_begin_grab_op (MetaDisplay *display, MetaScreen *screen, MetaWindow *window, MetaGrabOp op, gboolean pointer_already_grabbed, gboolean frame_action, int button, gulong modmask, guint32 timestamp, int root_x, int root_y); void meta_display_end_grab_op (MetaDisplay *display, guint32 timestamp); MetaGrabOp meta_display_get_grab_op (MetaDisplay *display); guint meta_display_add_keybinding (MetaDisplay *display, const char *name, GSettings *settings, MetaKeyBindingFlags flags, MetaKeyHandlerFunc handler, gpointer user_data, GDestroyNotify free_data); gboolean meta_display_remove_keybinding (MetaDisplay *display, const char *name); guint meta_display_grab_accelerator (MetaDisplay *display, const char *accelerator); gboolean meta_display_ungrab_accelerator (MetaDisplay *display, guint action_id); guint meta_display_get_keybinding_action (MetaDisplay *display, unsigned int keycode, unsigned long mask); /* meta_display_set_input_focus_window is like XSetInputFocus, except * that (a) it can't detect timestamps later than the current time, * since Ukwm isn't part of the XServer, and thus gives erroneous * behavior in this circumstance (so don't do it), (b) it uses * display->last_focus_time since we don't have access to the true * Xserver one, (c) it makes use of display->user_time since checking * whether a window should be allowed to be focused should depend * on user_time events (see bug 167358, comment 15 in particular) */ void meta_display_set_input_focus_window (MetaDisplay *display, MetaWindow *window, gboolean focus_frame, guint32 timestamp); /* meta_display_focus_the_no_focus_window is called when the * designated no_focus_window should be focused, but is otherwise the * same as meta_display_set_input_focus_window */ void meta_display_focus_the_no_focus_window (MetaDisplay *display, MetaScreen *screen, guint32 timestamp); GSList *meta_display_sort_windows_by_stacking (MetaDisplay *display, GSList *windows); void meta_display_add_ignored_crossing_serial (MetaDisplay *display, unsigned long serial); void meta_display_unmanage_screen (MetaDisplay *display, MetaScreen *screen, guint32 timestamp); void meta_display_clear_mouse_mode (MetaDisplay *display); void meta_display_freeze_keyboard (MetaDisplay *display, guint32 timestamp); void meta_display_ungrab_keyboard (MetaDisplay *display, guint32 timestamp); void meta_display_unfreeze_keyboard (MetaDisplay *display, guint32 timestamp); gboolean meta_display_is_pointer_emulating_sequence (MetaDisplay *display, ClutterEventSequence *sequence); void meta_display_request_pad_osd (MetaDisplay *display, ClutterInputDevice *pad, gboolean edition_mode); gchar * meta_display_get_pad_action_label (MetaDisplay *display, ClutterInputDevice *pad, MetaPadActionType action_type, guint action_number); #endif ukwm/src/meta/meta-settings.h0000664000175000017500000000204213233511035015127 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef META_SETTINGS_H #define META_SETTINGS_H #include "meta/types.h" int meta_settings_get_ui_scaling_factor (MetaSettings *settings); int meta_settings_get_font_dpi (MetaSettings *settings); #endif /* META_SETTINGS_H */ ukwm/src/meta/group.h0000664000175000017500000000335113220600404013476 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm window groups */ /* * Copyright (C) 2002 Red Hat Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_GROUP_H #define META_GROUP_H #include #include #include /* note, can return NULL */ MetaGroup* meta_window_get_group (MetaWindow *window); void meta_window_compute_group (MetaWindow* window); void meta_window_shutdown_group (MetaWindow *window); void meta_window_group_leader_changed (MetaWindow *window); /* note, can return NULL */ MetaGroup* meta_display_lookup_group (MetaDisplay *display, Window group_leader); GSList* meta_group_list_windows (MetaGroup *group); void meta_group_update_layers (MetaGroup *group); const char* meta_group_get_startup_id (MetaGroup *group); int meta_group_get_size (MetaGroup *group); gboolean meta_group_property_notify (MetaGroup *group, XEvent *event); #endif ukwm/src/meta/util.h0000664000175000017500000001464013220600404013322 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm utilities */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2005 Elijah Newren * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_UTIL_H #define META_UTIL_H #include #include #include gboolean meta_is_verbose (void); gboolean meta_is_debugging (void); gboolean meta_is_syncing (void); gboolean meta_is_wayland_compositor (void); void meta_debug_spew_real (const char *format, ...) G_GNUC_PRINTF (1, 2); void meta_verbose_real (const char *format, ...) G_GNUC_PRINTF (1, 2); void meta_bug (const char *format, ...) G_GNUC_PRINTF (1, 2); void meta_warning (const char *format, ...) G_GNUC_PRINTF (1, 2); 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 } MetaDebugTopic; void meta_topic_real (MetaDebugTopic topic, const char *format, ...) G_GNUC_PRINTF (2, 3); void meta_add_verbose_topic (MetaDebugTopic topic); void meta_remove_verbose_topic (MetaDebugTopic topic); void meta_push_no_msg_prefix (void); void meta_pop_no_msg_prefix (void); gint meta_unsigned_long_equal (gconstpointer v1, gconstpointer v2); guint meta_unsigned_long_hash (gconstpointer v); const char* meta_frame_type_to_string (MetaFrameType type); const char* meta_gravity_to_string (int gravity); char* meta_external_binding_name_for_action (guint keybinding_action); char* meta_g_utf8_strndup (const gchar *src, gsize n); void meta_free_gslist_and_elements (GSList *list_to_deep_free); 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 char *icon_name, 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 Ukwm to compute which windows should be mapped * @META_LATER_CHECK_FULLSCREEN: used by Ukwm to see if there's a fullscreen window * @META_LATER_SYNC_STACK: used by Ukwm 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; guint meta_later_add (MetaLaterType when, GSourceFunc func, gpointer data, GDestroyNotify notify); void meta_later_remove (guint later_id); typedef enum { META_LOCALE_DIRECTION_LTR, META_LOCALE_DIRECTION_RTL, } MetaLocaleDirection; MetaLocaleDirection meta_get_locale_direction (void); #endif /* META_UTIL_H */ ukwm/src/meta/meta-version.h.in0000664000175000017500000000210213220600404015351 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Rico Tzschichholz * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_VERSION_H #define META_VERSION_H #define META_MAJOR_VERSION @UKWM_MAJOR_VERSION@ #define META_MINOR_VERSION @UKWM_MINOR_VERSION@ #define META_MICRO_VERSION @UKWM_MICRO_VERSION@ #define META_PLUGIN_API_VERSION @UKWM_PLUGIN_API_VERSION@ #endif ukwm/src/meta/meta-idle-monitor.h0000664000175000017500000000531613220600404015673 0ustar fengfeng/* -*- 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; GType meta_idle_monitor_get_type (void); typedef void (*MetaIdleMonitorWatchFunc) (MetaIdleMonitor *monitor, guint watch_id, gpointer user_data); MetaIdleMonitor *meta_idle_monitor_get_core (void); MetaIdleMonitor *meta_idle_monitor_get_for_device (int device_id); guint meta_idle_monitor_add_idle_watch (MetaIdleMonitor *monitor, guint64 interval_msec, MetaIdleMonitorWatchFunc callback, gpointer user_data, GDestroyNotify notify); guint meta_idle_monitor_add_user_active_watch (MetaIdleMonitor *monitor, MetaIdleMonitorWatchFunc callback, gpointer user_data, GDestroyNotify notify); void meta_idle_monitor_remove_watch (MetaIdleMonitor *monitor, guint id); gint64 meta_idle_monitor_get_idletime (MetaIdleMonitor *monitor); #endif ukwm/src/meta/meta-plugin.h0000664000175000017500000004207613220600404014573 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 Intel Corp. * Copyright (c) 2017 Tianjin KYLIN Information Technology Co., Ltd. * * 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 #include #include #include #include #include #include #define META_TYPE_PLUGIN (meta_plugin_get_type ()) #define META_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_PLUGIN, MetaPlugin)) #define META_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_PLUGIN, MetaPluginClass)) #define META_IS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_PLUGIN)) #define META_IS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_PLUGIN)) #define META_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_PLUGIN, MetaPluginClass)) typedef struct _MetaPlugin MetaPlugin; typedef struct _MetaPluginClass MetaPluginClass; typedef struct _MetaPluginVersion MetaPluginVersion; typedef struct _MetaPluginInfo MetaPluginInfo; typedef struct _MetaPluginPrivate MetaPluginPrivate; struct _MetaPlugin { GObject parent; MetaPluginPrivate *priv; }; /** * 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); }; /** * 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; }; GType meta_plugin_get_type (void); 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(ObjectName, object_name) \ G_MODULE_EXPORT MetaPluginVersion meta_plugin_version = \ { \ META_MAJOR_VERSION, \ META_MINOR_VERSION, \ META_MICRO_VERSION, \ META_PLUGIN_API_VERSION \ }; \ \ static GType g_define_type_id = 0; \ \ /* Prototypes */ \ G_MODULE_EXPORT \ GType object_name##_get_type (void); \ \ G_MODULE_EXPORT \ GType object_name##_register_type (GTypeModule *type_module); \ \ G_MODULE_EXPORT \ GType meta_plugin_register_type (GTypeModule *type_module); \ \ GType \ object_name##_get_type (void) \ { \ return g_define_type_id; \ } \ \ static void object_name##_init (ObjectName *self); \ static void object_name##_class_init (ObjectName##Class *klass); \ static gpointer object_name##_parent_class = NULL; \ static void object_name##_class_intern_init (gpointer klass) \ { \ object_name##_parent_class = g_type_class_peek_parent (klass); \ object_name##_class_init ((ObjectName##Class *) klass); \ } \ \ GType \ object_name##_register_type (GTypeModule *type_module) \ { \ static const GTypeInfo our_info = \ { \ sizeof (ObjectName##Class), \ NULL, /* base_init */ \ NULL, /* base_finalize */ \ (GClassInitFunc) object_name##_class_intern_init, \ NULL, \ NULL, /* class_data */ \ sizeof (ObjectName), \ 0, /* n_preallocs */ \ (GInstanceInitFunc) object_name##_init \ }; \ \ g_define_type_id = g_type_module_register_type (type_module, \ META_TYPE_PLUGIN, \ #ObjectName, \ &our_info, \ 0); \ \ \ return g_define_type_id; \ } \ \ G_MODULE_EXPORT GType \ meta_plugin_register_type (GTypeModule *type_module) \ { \ return object_name##_register_type (type_module); \ } \ void meta_plugin_switch_workspace_completed (MetaPlugin *plugin); void meta_plugin_minimize_completed (MetaPlugin *plugin, MetaWindowActor *actor); void meta_plugin_unminimize_completed (MetaPlugin *plugin, MetaWindowActor *actor); void meta_plugin_size_change_completed (MetaPlugin *plugin, MetaWindowActor *actor); void meta_plugin_map_completed (MetaPlugin *plugin, MetaWindowActor *actor); void meta_plugin_destroy_completed (MetaPlugin *plugin, MetaWindowActor *actor); 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; gboolean meta_plugin_begin_modal (MetaPlugin *plugin, MetaModalOptions options, guint32 timestamp); void meta_plugin_end_modal (MetaPlugin *plugin, guint32 timestamp); MetaScreen *meta_plugin_get_screen (MetaPlugin *plugin); void _meta_plugin_set_compositor (MetaPlugin *plugin, MetaCompositor *compositor); /* XXX: Putting this in here so it's in the public header. */ void meta_plugin_manager_set_plugin_type (GType gtype); #endif /* META_PLUGIN_H_ */ ukwm/src/meta/common.h0000664000175000017500000004437713220600404013647 0ustar fengfeng/* -*- 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 * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_COMMON_H #define META_COMMON_H /* Don't include core headers here */ #include #include #include #include #include #include /** * SECTION:common * @Title: Common * @Short_Description: Ukwm 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 /** * MetaFrameFlags: * @META_FRAME_ALLOWS_DELETE: frame allows delete * @META_FRAME_ALLOWS_MENU: frame allows menu * @META_FRAME_ALLOWS_APPMENU: frame allows (fallback) app 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_IS_FLASHING: frame is flashing * @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_APPMENU = 1 << 2, META_FRAME_ALLOWS_MINIMIZE = 1 << 3, META_FRAME_ALLOWS_MAXIMIZE = 1 << 4, META_FRAME_ALLOWS_VERTICAL_RESIZE = 1 << 5, META_FRAME_ALLOWS_HORIZONTAL_RESIZE = 1 << 6, META_FRAME_HAS_FOCUS = 1 << 7, META_FRAME_SHADED = 1 << 8, META_FRAME_STUCK = 1 << 9, META_FRAME_MAXIMIZED = 1 << 10, META_FRAME_ALLOWS_SHADE = 1 << 11, META_FRAME_ALLOWS_MOVE = 1 << 12, META_FRAME_FULLSCREEN = 1 << 13, META_FRAME_IS_FLASHING = 1 << 14, META_FRAME_ABOVE = 1 << 15, META_FRAME_TILED_LEFT = 1 << 16, META_FRAME_TILED_RIGHT = 1 << 17 } MetaFrameFlags; /** * 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, }; #define GRAB_OP_GET_BASE_TYPE(op) (op & 0x00FF) 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 * screen.[ch]:MetaScreenDirection, * 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_APPMENU, 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=5): * @right_buttons: (array fixed-size=5): * @left_buttons_has_spacer: (array fixed-size=5): * @right_buttons_has_spacer: (array fixed-size=5): */ 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 */ 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: * Ukwm plugin unloading */ #define META_PRIORITY_PREFS_NOTIFY (G_PRIORITY_DEFAULT_IDLE + 10) /************************************************************/ #define POINT_IN_RECT(xcoord, ycoord, rect) \ ((xcoord) >= (rect).x && \ (xcoord) < ((rect).x + (rect).width) && \ (ycoord) >= (rect).y && \ (ycoord) < ((rect).y + (rect).height)) /** * 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; #endif ukwm/src/meta/barrier.h0000664000175000017500000000675713220600404014005 0ustar fengfeng/* -*- 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; }; GType meta_barrier_get_type (void) G_GNUC_CONST; gboolean meta_barrier_is_active (MetaBarrier *barrier); void meta_barrier_destroy (MetaBarrier *barrier); 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 ()) GType meta_barrier_event_get_type (void) G_GNUC_CONST; G_END_DECLS #endif /* __META_BARRIER_H__ */ ukwm/src/meta/window.h0000664000175000017500000003005513220600404013652 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2008 Iain Holmes * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, 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)) typedef struct _MetaWindowClass MetaWindowClass; GType meta_window_get_type (void); MetaFrame *meta_window_get_frame (MetaWindow *window); gboolean meta_window_has_focus (MetaWindow *window); gboolean meta_window_appears_focused (MetaWindow *window); gboolean meta_window_is_shaded (MetaWindow *window); gboolean meta_window_is_override_redirect (MetaWindow *window); gboolean meta_window_is_skip_taskbar (MetaWindow *window); void meta_window_get_buffer_rect (const MetaWindow *window, MetaRectangle *rect); void meta_window_get_frame_rect (const MetaWindow *window, MetaRectangle *rect); void meta_window_client_rect_to_frame_rect (MetaWindow *window, MetaRectangle *client_rect, MetaRectangle *frame_rect); void meta_window_frame_rect_to_client_rect (MetaWindow *window, MetaRectangle *frame_rect, MetaRectangle *client_rect); MetaScreen *meta_window_get_screen (MetaWindow *window); MetaDisplay *meta_window_get_display (MetaWindow *window); Window meta_window_get_xwindow (MetaWindow *window); MetaWindowType meta_window_get_window_type (MetaWindow *window); MetaWorkspace *meta_window_get_workspace (MetaWindow *window); int meta_window_get_monitor (MetaWindow *window); gboolean meta_window_is_on_all_workspaces (MetaWindow *window); gboolean meta_window_located_on_workspace (MetaWindow *window, MetaWorkspace *workspace); gboolean meta_window_is_hidden (MetaWindow *window); void meta_window_activate (MetaWindow *window,guint32 current_time); void meta_window_activate_with_workspace (MetaWindow *window, guint32 current_time, MetaWorkspace *workspace); const char * meta_window_get_description (MetaWindow *window); const char * meta_window_get_wm_class (MetaWindow *window); const char * meta_window_get_wm_class_instance (MetaWindow *window); gboolean meta_window_showing_on_its_workspace (MetaWindow *window); const char * meta_window_get_flatpak_id (MetaWindow *window); const char * meta_window_get_gtk_theme_variant (MetaWindow *window); const char * meta_window_get_gtk_application_id (MetaWindow *window); const char * meta_window_get_gtk_unique_bus_name (MetaWindow *window); const char * meta_window_get_gtk_application_object_path (MetaWindow *window); const char * meta_window_get_gtk_window_object_path (MetaWindow *window); const char * meta_window_get_gtk_app_menu_object_path (MetaWindow *window); const char * meta_window_get_gtk_menubar_object_path (MetaWindow *window); void meta_window_move_frame(MetaWindow *window, gboolean user_op, int root_x_nw, int root_y_nw); void meta_window_move_resize_frame (MetaWindow *window, gboolean user_op, int root_x_nw, int root_y_nw, int w, int h); void meta_window_move_to_monitor (MetaWindow *window, int monitor); void meta_window_set_demands_attention (MetaWindow *window); void meta_window_unset_demands_attention (MetaWindow *window); const char* meta_window_get_startup_id (MetaWindow *window); void meta_window_change_workspace_by_index (MetaWindow *window, gint space_index, gboolean append); void meta_window_change_workspace (MetaWindow *window, MetaWorkspace *workspace); GObject *meta_window_get_compositor_private (MetaWindow *window); void meta_window_set_compositor_private (MetaWindow *window, GObject *priv); const char *meta_window_get_role (MetaWindow *window); MetaStackLayer meta_window_get_layer (MetaWindow *window); MetaWindow* meta_window_find_root_ancestor (MetaWindow *window); gboolean meta_window_is_ancestor_of_transient (MetaWindow *window, MetaWindow *transient); typedef gboolean (*MetaWindowForeachFunc) (MetaWindow *window, void *user_data); void meta_window_foreach_transient (MetaWindow *window, MetaWindowForeachFunc func, void *user_data); void meta_window_foreach_ancestor (MetaWindow *window, MetaWindowForeachFunc func, void *user_data); MetaMaximizeFlags meta_window_get_maximized (MetaWindow *window); gboolean meta_window_is_fullscreen (MetaWindow *window); gboolean meta_window_is_screen_sized (MetaWindow *window); gboolean meta_window_is_monitor_sized (MetaWindow *window); gboolean meta_window_is_on_primary_monitor (MetaWindow *window); gboolean meta_window_requested_bypass_compositor (MetaWindow *window); gboolean meta_window_requested_dont_bypass_compositor (MetaWindow *window); gboolean meta_window_get_icon_geometry (MetaWindow *window, MetaRectangle *rect); void meta_window_set_icon_geometry (MetaWindow *window, MetaRectangle *rect); void meta_window_maximize (MetaWindow *window, MetaMaximizeFlags directions); void meta_window_unmaximize (MetaWindow *window, MetaMaximizeFlags directions); void meta_window_minimize (MetaWindow *window); void meta_window_unminimize (MetaWindow *window); void meta_window_raise (MetaWindow *window); void meta_window_lower (MetaWindow *window); const char *meta_window_get_title (MetaWindow *window); MetaWindow *meta_window_get_transient_for (MetaWindow *window); void meta_window_delete (MetaWindow *window, guint32 timestamp); guint meta_window_get_stable_sequence (MetaWindow *window); guint32 meta_window_get_user_time (MetaWindow *window); int meta_window_get_pid (MetaWindow *window); const char *meta_window_get_client_machine (MetaWindow *window); gboolean meta_window_is_remote (MetaWindow *window); gboolean meta_window_is_attached_dialog (MetaWindow *window); const char *meta_window_get_ukwm_hints (MetaWindow *window); MetaFrameType meta_window_get_frame_type (MetaWindow *window); cairo_region_t *meta_window_get_frame_bounds (MetaWindow *window); MetaWindow *meta_window_get_tile_match (MetaWindow *window); void meta_window_make_fullscreen (MetaWindow *window); void meta_window_unmake_fullscreen (MetaWindow *window); void meta_window_make_above (MetaWindow *window); void meta_window_unmake_above (MetaWindow *window); void meta_window_shade (MetaWindow *window, guint32 timestamp); void meta_window_unshade (MetaWindow *window, guint32 timestamp); void meta_window_stick (MetaWindow *window); void meta_window_unstick (MetaWindow *window); void meta_window_kill (MetaWindow *window); void meta_window_focus (MetaWindow *window, guint32 timestamp); void meta_window_check_alive (MetaWindow *window, guint32 timestamp); void meta_window_get_work_area_current_monitor (MetaWindow *window, MetaRectangle *area); void meta_window_get_work_area_for_monitor (MetaWindow *window, int which_monitor, MetaRectangle *area); void meta_window_get_work_area_all_monitors (MetaWindow *window, MetaRectangle *area); void meta_window_begin_grab_op (MetaWindow *window, MetaGrabOp op, gboolean frame_action, guint32 timestamp); gboolean meta_window_can_maximize (MetaWindow *window); gboolean meta_window_can_minimize (MetaWindow *window); gboolean meta_window_can_shade (MetaWindow *window); gboolean meta_window_can_close (MetaWindow *window); gboolean meta_window_is_always_on_all_workspaces (MetaWindow *window); gboolean meta_window_is_above (MetaWindow *window); gboolean meta_window_allows_move (MetaWindow *window); gboolean meta_window_allows_resize (MetaWindow *window); gboolean meta_window_is_client_decorated (MetaWindow *window); gboolean meta_window_titlebar_is_onscreen (MetaWindow *window); void meta_window_shove_titlebar_onscreen (MetaWindow *window); #endif ukwm/src/meta/keybindings.h0000664000175000017500000000271013220600404014646 0ustar fengfeng/* * 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 ()) const char *meta_key_binding_get_name (MetaKeyBinding *binding); MetaVirtualModifier meta_key_binding_get_modifiers (MetaKeyBinding *binding); guint meta_key_binding_get_mask (MetaKeyBinding *binding); gboolean meta_key_binding_is_builtin (MetaKeyBinding *binding); gboolean meta_key_binding_is_reversed (MetaKeyBinding *binding); gboolean meta_keybindings_set_custom_handler (const gchar *name, MetaKeyHandlerFunc handler, gpointer user_data, GDestroyNotify free_data); #endif ukwm/src/meta/meta-cursor-tracker.h0000664000175000017500000000511713220600404016236 0ustar fengfeng/* -*- 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 #include #include #include #define META_TYPE_CURSOR_TRACKER (meta_cursor_tracker_get_type ()) #define META_CURSOR_TRACKER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_CURSOR_TRACKER, MetaCursorTracker)) #define META_CURSOR_TRACKER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_CURSOR_TRACKER, MetaCursorTrackerClass)) #define META_IS_CURSOR_TRACKER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_CURSOR_TRACKER)) #define META_IS_CURSOR_TRACKER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_CURSOR_TRACKER)) #define META_CURSOR_TRACKER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_CURSOR_TRACKER, MetaCursorTrackerClass)) typedef struct _MetaCursorTrackerClass MetaCursorTrackerClass; GType meta_cursor_tracker_get_type (void); MetaCursorTracker *meta_cursor_tracker_get_for_screen (MetaScreen *screen); void meta_cursor_tracker_get_hot (MetaCursorTracker *tracker, int *x, int *y); CoglTexture *meta_cursor_tracker_get_sprite (MetaCursorTracker *tracker); void meta_cursor_tracker_get_pointer (MetaCursorTracker *tracker, int *x, int *y, ClutterModifierType *mods); void meta_cursor_tracker_set_pointer_visible (MetaCursorTracker *tracker, gboolean visible); #endif ukwm/src/meta/meta-background-actor.h0000664000175000017500000000657213220600404016523 0ustar fengfeng/* -*- 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 #include #include /** * 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 ()) #define META_BACKGROUND_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BACKGROUND_ACTOR, MetaBackgroundActor)) #define META_BACKGROUND_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BACKGROUND_ACTOR, MetaBackgroundActorClass)) #define META_IS_BACKGROUND_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BACKGROUND_ACTOR)) #define META_IS_BACKGROUND_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BACKGROUND_ACTOR)) #define META_BACKGROUND_ACTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BACKGROUND_ACTOR, MetaBackgroundActorClass)) typedef struct _MetaBackgroundActor MetaBackgroundActor; typedef struct _MetaBackgroundActorClass MetaBackgroundActorClass; typedef struct _MetaBackgroundActorPrivate MetaBackgroundActorPrivate; struct _MetaBackgroundActorClass { /*< private >*/ ClutterActorClass parent_class; }; struct _MetaBackgroundActor { ClutterActor parent; MetaBackgroundActorPrivate *priv; }; GType meta_background_actor_get_type (void); ClutterActor *meta_background_actor_new (MetaScreen *screen, int monitor); void meta_background_actor_set_background (MetaBackgroundActor *self, MetaBackground *background); void meta_background_actor_set_gradient (MetaBackgroundActor *self, gboolean enabled, int height, double tone_start); void meta_background_actor_set_monitor (MetaBackgroundActor *self, int monitor); void meta_background_actor_set_vignette (MetaBackgroundActor *self, gboolean enabled, double brightness, double sharpness); #endif /* META_BACKGROUND_ACTOR_H */ ukwm/src/meta/meta-inhibit-shortcuts-dialog.h0000664000175000017500000000333613220600404020210 0ustar fengfeng/* * 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 ()) 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); }; void meta_inhibit_shortcuts_dialog_show (MetaInhibitShortcutsDialog *dialog); void meta_inhibit_shortcuts_dialog_hide (MetaInhibitShortcutsDialog *dialog); void meta_inhibit_shortcuts_dialog_response (MetaInhibitShortcutsDialog *dialog, MetaInhibitShortcutsDialogResponse response); #endif /* META_INHIBIT_SHORTCUTS_DIALOG_H */ ukwm/src/meta/main.h0000664000175000017500000000353713220600404013274 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm main */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_MAIN_H #define META_MAIN_H #include GOptionContext *meta_get_option_context (void); void meta_init (void); int meta_run (void); void meta_register_with_session (void); gboolean meta_activate_session (void); /* Actually defined in meta-backend.c */ gboolean meta_get_replace_current_wm (void); /* Actually defined in util.c */ void meta_set_wm_name (const char *wm_name); void meta_set_gnome_wm_keybindings (const char *wm_keybindings); void meta_restart (const char *message); 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 */ void meta_exit (MetaExitCode code) G_GNUC_NORETURN; /* g_main_loop_quit() then fall out of main() */ void meta_quit (MetaExitCode code); #endif ukwm/src/meta/meta-close-dialog.h0000664000175000017500000000351213220600404015627 0ustar fengfeng/* -*- 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 ()) 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); }; void meta_close_dialog_show (MetaCloseDialog *dialog); void meta_close_dialog_hide (MetaCloseDialog *dialog); void meta_close_dialog_focus (MetaCloseDialog *dialog); gboolean meta_close_dialog_is_visible (MetaCloseDialog *dialog); void meta_close_dialog_response (MetaCloseDialog *dialog, MetaCloseDialogResponse response); #endif /* META_CLOSE_DIALOG_H */ ukwm/src/meta/meta-background-image.h0000664000175000017500000000756213220600404016475 0ustar fengfeng/* -*- 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 #define META_TYPE_BACKGROUND_IMAGE (meta_background_image_get_type ()) #define META_BACKGROUND_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BACKGROUND_IMAGE, MetaBackgroundImage)) #define META_BACKGROUND_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BACKGROUND_IMAGE, MetaBackgroundImageClass)) #define META_IS_BACKGROUND_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BACKGROUND_IMAGE)) #define META_IS_BACKGROUND_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BACKGROUND_IMAGE)) #define META_BACKGROUND_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BACKGROUND_IMAGE, MetaBackgroundImageClass)) /** * MetaBackgroundImage: * * #MetaBackgroundImage is an object that represents a loaded or loading background image. */ typedef struct _MetaBackgroundImage MetaBackgroundImage; typedef struct _MetaBackgroundImageClass MetaBackgroundImageClass; GType meta_background_image_get_type (void); gboolean meta_background_image_is_loaded (MetaBackgroundImage *image); gboolean meta_background_image_get_success (MetaBackgroundImage *image); CoglTexture *meta_background_image_get_texture (MetaBackgroundImage *image); #define META_TYPE_BACKGROUND_IMAGE_CACHE (meta_background_image_cache_get_type ()) #define META_BACKGROUND_IMAGE_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BACKGROUND_IMAGE_CACHE, MetaBackgroundImageCache)) #define META_BACKGROUND_IMAGE_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BACKGROUND_IMAGE_CACHE, MetaBackgroundImageCacheClass)) #define META_IS_BACKGROUND_IMAGE_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BACKGROUND_IMAGE_CACHE)) #define META_IS_BACKGROUND_IMAGE_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BACKGROUND_IMAGE_CACHE)) #define META_BACKGROUND_IMAGE_CACHE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BACKGROUND_IMAGE_CACHE, MetaBackgroundImageCacheClass)) /** * 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. */ typedef struct _MetaBackgroundImageCache MetaBackgroundImageCache; typedef struct _MetaBackgroundImageCacheClass MetaBackgroundImageCacheClass; MetaBackgroundImageCache *meta_background_image_cache_get_default (void); GType meta_background_image_cache_get_type (void); MetaBackgroundImage *meta_background_image_cache_load (MetaBackgroundImageCache *cache, GFile *file); void meta_background_image_cache_purge (MetaBackgroundImageCache *cache, GFile *file); #endif /* __META_BACKGROUND_IMAGE_H__ */ ukwm/src/meta/meta-dnd.h0000664000175000017500000000175213220600404014036 0ustar fengfeng/* -*- 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 "meta/types.h" #define META_TYPE_DND (meta_dnd_get_type ()) G_DECLARE_FINAL_TYPE (MetaDnd, meta_dnd, META, DND, GObject) #endif /* META_DND_H */ ukwm/src/meta/meta-shaped-texture.h0000664000175000017500000000633613220600404016236 0ustar fengfeng/* * 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 G_BEGIN_DECLS #define META_TYPE_SHAPED_TEXTURE (meta_shaped_texture_get_type()) #define META_SHAPED_TEXTURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),META_TYPE_SHAPED_TEXTURE, MetaShapedTexture)) #define META_SHAPED_TEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_SHAPED_TEXTURE, MetaShapedTextureClass)) #define META_IS_SHAPED_TEXTURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_SHAPED_TEXTURE)) #define META_IS_SHAPED_TEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_SHAPED_TEXTURE)) #define META_SHAPED_TEXTURE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_SHAPED_TEXTURE, MetaShapedTextureClass)) typedef struct _MetaShapedTexture MetaShapedTexture; typedef struct _MetaShapedTextureClass MetaShapedTextureClass; typedef struct _MetaShapedTexturePrivate MetaShapedTexturePrivate; struct _MetaShapedTextureClass { /*< private >*/ ClutterActorClass parent_class; }; /** * MetaShapedTexture: * * The MetaShapedTexture structure contains * only private data and should be accessed using the provided API */ struct _MetaShapedTexture { /*< private >*/ ClutterActor parent; MetaShapedTexturePrivate *priv; }; GType meta_shaped_texture_get_type (void) G_GNUC_CONST; void meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex, gboolean create_mipmaps); gboolean meta_shaped_texture_update_area (MetaShapedTexture *stex, int x, int y, int width, int height); CoglTexture * meta_shaped_texture_get_texture (MetaShapedTexture *stex); void meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex, CoglTexture *mask_texture); void meta_shaped_texture_set_opaque_region (MetaShapedTexture *stex, cairo_region_t *opaque_region); cairo_surface_t * meta_shaped_texture_get_image (MetaShapedTexture *stex, cairo_rectangle_int_t *clip); G_END_DECLS #endif /* __META_SHAPED_TEXTURE_H__ */ ukwm/src/meta/meta-background.h0000664000175000017500000000641213220600404015406 0ustar fengfeng/* -*- 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 #include /** * 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 ()) #define META_BACKGROUND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BACKGROUND, MetaBackground)) #define META_BACKGROUND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BACKGROUND, MetaBackgroundClass)) #define META_IS_BACKGROUND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BACKGROUND)) #define META_IS_BACKGROUND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BACKGROUND)) #define META_BACKGROUND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BACKGROUND, MetaBackgroundClass)) typedef struct _MetaBackground MetaBackground; typedef struct _MetaBackgroundClass MetaBackgroundClass; typedef struct _MetaBackgroundPrivate MetaBackgroundPrivate; struct _MetaBackgroundClass { /*< private >*/ GObjectClass parent_class; }; struct _MetaBackground { GObject parent; MetaBackgroundPrivate *priv; }; void meta_background_refresh_all (void); GType meta_background_get_type (void); MetaBackground *meta_background_new (MetaScreen *screen); void meta_background_set_color (MetaBackground *self, ClutterColor *color); void meta_background_set_gradient (MetaBackground *self, GDesktopBackgroundShading shading_direction, ClutterColor *color, ClutterColor *second_color); void meta_background_set_file (MetaBackground *self, GFile *file, GDesktopBackgroundStyle style); void meta_background_set_blend (MetaBackground *self, GFile *file1, GFile *file2, double blend_factor, GDesktopBackgroundStyle style); #endif /* META_BACKGROUND_H */ ukwm/src/meta/meta-backend.h0000664000175000017500000000355013233511035014663 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_BACKEND_H #define META_BACKEND_H #include #include #include "meta/meta-dnd.h" typedef struct _MetaBackend MetaBackend; typedef struct _MetaBackendClass MetaBackendClass; GType meta_backend_get_type (void); MetaBackend * meta_get_backend (void); void meta_backend_set_keymap (MetaBackend *backend, const char *layouts, const char *variants, const char *options); void meta_backend_lock_layout_group (MetaBackend *backend, guint idx); void meta_backend_set_numlock (MetaBackend *backend, gboolean numlock_state); ClutterActor *meta_backend_get_stage (MetaBackend *backend); MetaDnd *meta_backend_get_dnd (MetaBackend *backend); MetaSettings *meta_backend_get_settings (MetaBackend *backend); void meta_clutter_init (void); #endif /* META_BACKEND_H */ ukwm/src/meta/meta-window-actor.h0000664000175000017500000000521713220600404015706 0ustar fengfeng/* -*- 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 #include #include /* * MetaWindowActor object (ClutterGroup sub-class) */ #define META_TYPE_WINDOW_ACTOR (meta_window_actor_get_type ()) #define META_WINDOW_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_WINDOW_ACTOR, MetaWindowActor)) #define META_WINDOW_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_WINDOW_ACTOR, MetaWindowActorClass)) #define META_IS_WINDOW_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_WINDOW_ACTOR)) #define META_IS_WINDOW_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_WINDOW_ACTOR)) #define META_WINDOW_ACTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_WINDOW_ACTOR, MetaWindowActorClass)) typedef struct _MetaWindowActor MetaWindowActor; typedef struct _MetaWindowActorClass MetaWindowActorClass; typedef struct _MetaWindowActorPrivate MetaWindowActorPrivate; struct _MetaWindowActorClass { /*< private >*/ ClutterActorClass parent_class; }; struct _MetaWindowActor { ClutterActor parent; MetaWindowActorPrivate *priv; }; GType meta_window_actor_get_type (void); Window meta_window_actor_get_x_window (MetaWindowActor *self); MetaWindow * meta_window_actor_get_meta_window (MetaWindowActor *self); ClutterActor * meta_window_actor_get_texture (MetaWindowActor *self); void meta_window_actor_sync_visibility (MetaWindowActor *self); gboolean meta_window_actor_is_destroyed (MetaWindowActor *self); typedef enum { META_SHADOW_MODE_AUTO, META_SHADOW_MODE_FORCED_OFF, META_SHADOW_MODE_FORCED_ON, } MetaShadowMode; #endif /* META_WINDOW_ACTOR_H */ ukwm/src/meta/meta-monitor-manager.h0000664000175000017500000000431613233511035016374 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 struct _MetaMonitorManagerClass MetaMonitorManagerClass; typedef struct _MetaMonitorManager MetaMonitorManager; GType meta_monitor_manager_get_type (void); MetaMonitorManager *meta_monitor_manager_get (void); gint meta_monitor_manager_get_monitor_for_output (MetaMonitorManager *manager, guint id); gint meta_monitor_manager_get_monitor_for_connector (MetaMonitorManager *manager, const char *connector); gboolean meta_monitor_manager_get_is_builtin_display_on (MetaMonitorManager *manager); void meta_monitor_manager_switch_config (MetaMonitorManager *manager, MetaMonitorSwitchConfigType config_type); gboolean meta_monitor_manager_can_switch_config (MetaMonitorManager *manager); MetaMonitorSwitchConfigType meta_monitor_manager_get_switch_config (MetaMonitorManager *manager); gint meta_monitor_manager_get_display_configuration_timeout (void); #endif /* META_MONITOR_MANAGER_H */ ukwm/src/libukwm.pc.in0000664000175000017500000000061413220600404013645 0ustar fengfengprefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ girdir=@libdir@/ukwm typelibdir=@libdir@/ukwm apiversion=@LIBUKWM_API_VERSION@ Name: libukwm Description: Ukwm window manager library Requires: gsettings-desktop-schemas gtk+-3.0 ukwm-clutter-@LIBUKWM_API_VERSION@ x11 Version: @VERSION@ Libs: -L${libdir} -lukwm-@LIBUKWM_API_VERSION@ Cflags: -I${includedir}/ukwm ukwm/src/compositor/0000775000175000017500000000000013254604522013453 5ustar fengfengukwm/src/compositor/cogl-utils.c0000664000175000017500000001243413220600404015671 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Utilities for use with Cogl * * Copyright 2010 Red Hat, Inc. * Copyright 2010 Intel Corporation * Copyright 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include #include "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 * Ukwm. * * 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, /* layer */ COGL_TEXTURE_TYPE_2D); } pipeline = cogl_pipeline_copy (texture_pipeline_template); if (src_texture != NULL) cogl_pipeline_set_layer_texture (pipeline, 0, src_texture); return pipeline; } static gboolean is_pot(int x) { return x > 0 && (x & (x - 1)) == 0; } /** * 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 non-power-of-two textures are not supported on the system, then * the texture will be created as a texture rectangle; in this case, * hardware repeating isn't possible, and texture coordinates are also * different, but Cogl hides these issues from the application, except from * GLSL shaders. Since GLSL is never (or at least almost never) * present on such a system, this is not typically an issue. * * 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; gboolean should_use_rectangle = FALSE; if (!(is_pot (width) && is_pot (height)) && !cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT)) { if (cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_RECTANGLE)) should_use_rectangle = TRUE; else g_error ("Cannot create texture. Support for GL_ARB_texture_non_power_of_two or " "ARB_texture_rectangle is required"); } if (should_use_rectangle) texture = COGL_TEXTURE (cogl_texture_rectangle_new_with_size (ctx, width, height)); else 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 */ CoglError *catch_error = NULL; if (!cogl_texture_allocate (texture, &catch_error)) { cogl_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; } ukwm/src/compositor/meta-window-shape.c0000664000175000017500000001612213220600404017136 0ustar fengfeng/* -*- 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 #include #include "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) ukwm/src/compositor/meta-shadow-factory.c0000664000175000017500000010553513220600404017472 0ustar fengfeng/* -*- 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 #include #include #include #include #include "cogl-utils.h" #include "region-utils.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; }; struct _MetaShadowFactoryClass { GObjectClass parent_class; }; 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", { 0, -1, 0, 0, 0 }, { 0, -1, 0, 0, 0 } } }; 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, 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; cogl_pipeline_set_color4ub (shadow->pipeline, opacity, opacity, opacity, opacity); cogl_set_source (shadow->pipeline); 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; /* 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_rectangle_with_texture_coords (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_rectangle_with_texture_coords (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); CoglError *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); cogl_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) ukwm/src/compositor/meta-window-group.h0000664000175000017500000000316613220600404017203 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef META_WINDOW_GROUP_H #define META_WINDOW_GROUP_H #include #include /** * 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. */ #define META_TYPE_WINDOW_GROUP (meta_window_group_get_type ()) #define META_WINDOW_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_WINDOW_GROUP, MetaWindowGroup)) #define META_WINDOW_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_WINDOW_GROUP, MetaWindowGroupClass)) #define META_IS_WINDOW_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_WINDOW_GROUP)) #define META_IS_WINDOW_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_WINDOW_GROUP)) #define META_WINDOW_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_WINDOW_GROUP, MetaWindowGroupClass)) typedef struct _MetaWindowGroup MetaWindowGroup; typedef struct _MetaWindowGroupClass MetaWindowGroupClass; typedef struct _MetaWindowGroupPrivate MetaWindowGroupPrivate; GType meta_window_group_get_type (void); ClutterActor *meta_window_group_new (MetaScreen *screen); gboolean meta_window_group_actor_is_untransformed (ClutterActor *actor, int *x_origin, int *y_origin); #endif /* META_WINDOW_GROUP_H */ ukwm/src/compositor/meta-plugin-manager.h0000664000175000017500000001027713220600404017451 0ustar fengfeng/* -*- 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 #include #include 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); 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); #endif ukwm/src/compositor/meta-surface-actor-x11.c0000664000175000017500000003427313233511035017712 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Owen Taylor * Jasper St. Pierre */ #include "config.h" #include "meta-surface-actor-x11.h" #include #include #include #include "window-private.h" #include "meta-shaped-texture-private.h" #include "meta-cullable.h" #include "x11/window-x11.h" struct _MetaSurfaceActorX11Private { 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; }; typedef struct _MetaSurfaceActorX11Private MetaSurfaceActorX11Private; G_DEFINE_TYPE_WITH_PRIVATE (MetaSurfaceActorX11, meta_surface_actor_x11, META_TYPE_SURFACE_ACTOR) static void free_damage (MetaSurfaceActorX11 *self) { MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self); MetaDisplay *display = priv->display; Display *xdisplay = meta_display_get_xdisplay (display); if (priv->damage == None) return; meta_error_trap_push (display); XDamageDestroy (xdisplay, priv->damage); priv->damage = None; meta_error_trap_pop (display); } static void detach_pixmap (MetaSurfaceActorX11 *self) { MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self); MetaDisplay *display = priv->display; Display *xdisplay = meta_display_get_xdisplay (display); MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self)); if (priv->pixmap == None) return; /* 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_error_trap_push (display); XFreePixmap (xdisplay, priv->pixmap); priv->pixmap = None; meta_error_trap_pop (display); g_clear_pointer (&priv->texture, cogl_object_unref); } static void set_pixmap (MetaSurfaceActorX11 *self, Pixmap pixmap) { MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self); CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self)); CoglError *error = NULL; CoglTexture *texture; g_assert (priv->pixmap == None); priv->pixmap = pixmap; texture = COGL_TEXTURE (cogl_texture_pixmap_x11_new (ctx, priv->pixmap, FALSE, &error)); if (error != NULL) { g_warning ("Failed to allocate stex texture: %s", error->message); cogl_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"); priv->texture = texture; meta_shaped_texture_set_texture (stex, texture); } static void update_pixmap (MetaSurfaceActorX11 *self) { MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self); MetaDisplay *display = priv->display; Display *xdisplay = meta_display_get_xdisplay (display); if (priv->size_changed) { detach_pixmap (self); priv->size_changed = FALSE; } if (priv->pixmap == None) { Pixmap new_pixmap; Window xwindow = meta_window_x11_get_toplevel_xwindow (priv->window); meta_error_trap_push (display); new_pixmap = XCompositeNameWindowPixmap (xdisplay, xwindow); if (meta_error_trap_pop_with_return (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 (priv->window)); return; } set_pixmap (self, new_pixmap); } } static gboolean is_visible (MetaSurfaceActorX11 *self) { MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self); return (priv->pixmap != None) && !priv->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); MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self); priv->received_damage = TRUE; if (meta_window_is_fullscreen (priv->window) && !priv->unredirected && !priv->does_full_damage) { MetaRectangle window_rect; meta_window_get_frame_rect (priv->window, &window_rect); if (x == 0 && y == 0 && window_rect.width == width && window_rect.height == height) priv->full_damage_frames_count++; else priv->full_damage_frames_count = 0; if (priv->full_damage_frames_count >= 100) priv->does_full_damage = TRUE; } if (!is_visible (self)) return; cogl_texture_pixmap_x11_update_area (COGL_TEXTURE_PIXMAP_X11 (priv->texture), x, y, width, height); } static void meta_surface_actor_x11_pre_paint (MetaSurfaceActor *actor) { MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor); MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self); MetaDisplay *display = priv->display; Display *xdisplay = meta_display_get_xdisplay (display); if (priv->received_damage) { meta_error_trap_push (display); XDamageSubtract (xdisplay, priv->damage, None, None); meta_error_trap_pop (display); priv->received_damage = FALSE; } update_pixmap (self); } static gboolean meta_surface_actor_x11_is_visible (MetaSurfaceActor *actor) { MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor); return is_visible (self); } static gboolean meta_surface_actor_x11_is_opaque (MetaSurfaceActor *actor) { MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor); MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self); /* If we're not ARGB32, then we're opaque. */ if (!meta_surface_actor_is_argb32 (actor)) return TRUE; cairo_region_t *opaque_region = meta_surface_actor_get_opaque_region (actor); /* If we have no opaque region, then no pixels are opaque. */ if (!opaque_region) return FALSE; MetaWindow *window = priv->window; cairo_rectangle_int_t client_area; meta_window_get_client_area_rect (window, &client_area); /* Otherwise, check if our opaque region covers our entire surface. */ if (cairo_region_contains_rectangle (opaque_region, &client_area) == CAIRO_REGION_OVERLAP_IN) return TRUE; return FALSE; } static gboolean meta_surface_actor_x11_should_unredirect (MetaSurfaceActor *actor) { MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor); MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self); MetaWindow *window = priv->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 (actor)) return FALSE; if (meta_window_is_override_redirect (window)) return TRUE; if (priv->does_full_damage) return TRUE; return FALSE; } static void sync_unredirected (MetaSurfaceActorX11 *self) { MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self); MetaDisplay *display = priv->display; Display *xdisplay = meta_display_get_xdisplay (display); Window xwindow = meta_window_x11_get_toplevel_xwindow (priv->window); meta_error_trap_push (display); if (priv->unredirected) { detach_pixmap (self); XCompositeUnredirectWindow (xdisplay, xwindow, CompositeRedirectManual); } else { XCompositeRedirectWindow (xdisplay, xwindow, CompositeRedirectManual); } meta_error_trap_pop (display); } static void meta_surface_actor_x11_set_unredirected (MetaSurfaceActor *actor, gboolean unredirected) { MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor); MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self); if (priv->unredirected == unredirected) return; priv->unredirected = unredirected; sync_unredirected (self); } static gboolean meta_surface_actor_x11_is_unredirected (MetaSurfaceActor *actor) { MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor); MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self); return priv->unredirected; } static void meta_surface_actor_x11_dispose (GObject *object) { MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (object); detach_pixmap (self); free_damage (self); G_OBJECT_CLASS (meta_surface_actor_x11_parent_class)->dispose (object); } static MetaWindow * meta_surface_actor_x11_get_window (MetaSurfaceActor *actor) { MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (META_SURFACE_ACTOR_X11 (actor)); return priv->window; } 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_visible = meta_surface_actor_x11_is_visible; surface_actor_class->should_unredirect = meta_surface_actor_x11_should_unredirect; surface_actor_class->set_unredirected = meta_surface_actor_x11_set_unredirected; surface_actor_class->is_unredirected = meta_surface_actor_x11_is_unredirected; surface_actor_class->get_window = meta_surface_actor_x11_get_window; } static void meta_surface_actor_x11_init (MetaSurfaceActorX11 *self) { MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self); priv->last_width = -1; priv->last_height = -1; } static void create_damage (MetaSurfaceActorX11 *self) { MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self); Display *xdisplay = meta_display_get_xdisplay (priv->display); Window xwindow = meta_window_x11_get_toplevel_xwindow (priv->window); priv->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); detach_pixmap (self); free_damage (self); create_damage (self); } static void reset_texture (MetaSurfaceActorX11 *self) { MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self); MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self)); if (!priv->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, priv->texture); } MetaSurfaceActor * meta_surface_actor_x11_new (MetaWindow *window) { MetaSurfaceActorX11 *self = g_object_new (META_TYPE_SURFACE_ACTOR_X11, NULL); MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self); MetaDisplay *display = meta_window_get_display (window); g_assert (!meta_is_wayland_compositor ()); priv->window = window; priv->display = display; g_signal_connect_object (priv->display, "gl-video-memory-purged", G_CALLBACK (reset_texture), self, G_CONNECT_SWAPPED); create_damage (self); g_signal_connect_object (priv->window, "notify::decorated", G_CALLBACK (window_decorated_notify), self, 0); priv->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) { MetaSurfaceActorX11Private *priv = meta_surface_actor_x11_get_instance_private (self); MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self)); if (priv->last_width == width && priv->last_height == height) return; priv->size_changed = TRUE; priv->last_width = width; priv->last_height = height; meta_shaped_texture_set_fallback_size (stex, width, height); } ukwm/src/compositor/meta-dnd-actor.c0000664000175000017500000001534413220600404016411 0ustar fengfeng/* -*- 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 #include #include "meta-dnd-actor-private.h" #define DRAG_FAILED_DURATION 500 enum { PROP_DRAG_ORIGIN = 1, PROP_DRAG_START_X, PROP_DRAG_START_Y }; typedef struct _MetaDnDActorPrivate MetaDnDActorPrivate; struct _MetaDnDActorPrivate { ClutterActor *drag_origin; int drag_start_x; int drag_start_y; }; G_DEFINE_TYPE_WITH_PRIVATE (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); MetaDnDActorPrivate *priv = meta_dnd_actor_get_instance_private (self); switch (prop_id) { case PROP_DRAG_ORIGIN: priv->drag_origin = g_value_get_object (value); break; case PROP_DRAG_START_X: priv->drag_start_x = g_value_get_int (value); break; case PROP_DRAG_START_Y: priv->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); MetaDnDActorPrivate *priv = meta_dnd_actor_get_instance_private (self); switch (prop_id) { case PROP_DRAG_ORIGIN: g_value_set_object (value, priv->drag_origin); break; case PROP_DRAG_START_X: g_value_set_int (value, priv->drag_start_x); break; case PROP_DRAG_START_Y: g_value_set_int (value, priv->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) { MetaDnDActorPrivate *priv; ClutterActor *actor; g_return_if_fail (META_IS_DND_ACTOR (self)); actor = CLUTTER_ACTOR (self); priv = meta_dnd_actor_get_instance_private (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 (priv->drag_origin)) { int anchor_x, anchor_y; ClutterPoint dest; clutter_actor_get_transformed_position (priv->drag_origin, &dest.x, &dest.y); meta_feedback_actor_get_anchor (META_FEEDBACK_ACTOR (self), &anchor_x, &anchor_y); dest.x += priv->drag_start_x - anchor_x; dest.y += priv->drag_start_y - anchor_y; 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); } } ukwm/src/compositor/meta-cullable.c0000664000175000017500000001630213220600404016314 0ustar fengfeng/* -*- 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 "meta-cullable.h" #include "clutter-utils.h" G_DEFINE_INTERFACE (MetaCullable, meta_cullable, CLUTTER_TYPE_ACTOR); /** * 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 && clutter_actor_has_effects (child)) needs_culling = FALSE; if (needs_culling && !meta_actor_is_untransformed (child, NULL, NULL)) 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 void meta_cullable_default_init (MetaCullableInterface *iface) { } /** * 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_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); } ukwm/src/compositor/meta-surface-actor-wayland.h0000664000175000017500000000662413233511035020744 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #ifndef __META_SURFACE_ACTOR_WAYLAND_H__ #define __META_SURFACE_ACTOR_WAYLAND_H__ #include #include "meta-surface-actor.h" #include "wayland/meta-wayland.h" #include "wayland/meta-wayland-private.h" #include "backends/meta-monitor-manager-private.h" G_BEGIN_DECLS #define META_TYPE_SURFACE_ACTOR_WAYLAND (meta_surface_actor_wayland_get_type ()) #define META_SURFACE_ACTOR_WAYLAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_SURFACE_ACTOR_WAYLAND, MetaSurfaceActorWayland)) #define META_SURFACE_ACTOR_WAYLAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_SURFACE_ACTOR_WAYLAND, MetaSurfaceActorWaylandClass)) #define META_IS_SURFACE_ACTOR_WAYLAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_SURFACE_ACTOR_WAYLAND)) #define META_IS_SURFACE_ACTOR_WAYLAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_SURFACE_ACTOR_WAYLAND)) #define META_SURFACE_ACTOR_WAYLAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_SURFACE_ACTOR_WAYLAND, MetaSurfaceActorWaylandClass)) typedef struct _MetaSurfaceActorWayland MetaSurfaceActorWayland; typedef struct _MetaSurfaceActorWaylandClass MetaSurfaceActorWaylandClass; struct _MetaSurfaceActorWayland { MetaSurfaceActor parent; }; struct _MetaSurfaceActorWaylandClass { MetaSurfaceActorClass parent_class; }; GType meta_surface_actor_wayland_get_type (void); 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_sync_state (MetaSurfaceActorWayland *self); void meta_surface_actor_wayland_sync_state_recursive (MetaSurfaceActorWayland *self); void meta_surface_actor_wayland_sync_subsurface_state (MetaSurfaceActorWayland *self); gboolean meta_surface_actor_wayland_is_on_monitor (MetaSurfaceActorWayland *self, MetaLogicalMonitor *logical_monitor); void meta_surface_actor_wayland_add_frame_callbacks (MetaSurfaceActorWayland *self, struct wl_list *frame_callbacks); G_END_DECLS #endif /* __META_SURFACE_ACTOR_WAYLAND_H__ */ ukwm/src/compositor/meta-surface-actor-x11.h0000664000175000017500000000464113233511035017713 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Owen Taylor * Jasper St. Pierre */ #ifndef __META_SURFACE_ACTOR_X11_H__ #define __META_SURFACE_ACTOR_X11_H__ #include #include "meta-surface-actor.h" #include #include #include G_BEGIN_DECLS #define META_TYPE_SURFACE_ACTOR_X11 (meta_surface_actor_x11_get_type ()) #define META_SURFACE_ACTOR_X11(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_SURFACE_ACTOR_X11, MetaSurfaceActorX11)) #define META_SURFACE_ACTOR_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_SURFACE_ACTOR_X11, MetaSurfaceActorX11Class)) #define META_IS_SURFACE_ACTOR_X11(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_SURFACE_ACTOR_X11)) #define META_IS_SURFACE_ACTOR_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_SURFACE_ACTOR_X11)) #define META_SURFACE_ACTOR_X11_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_SURFACE_ACTOR_X11, MetaSurfaceActorX11Class)) typedef struct _MetaSurfaceActorX11 MetaSurfaceActorX11; typedef struct _MetaSurfaceActorX11Class MetaSurfaceActorX11Class; struct _MetaSurfaceActorX11 { MetaSurfaceActor parent; }; struct _MetaSurfaceActorX11Class { MetaSurfaceActorClass parent_class; }; GType meta_surface_actor_x11_get_type (void); MetaSurfaceActor * meta_surface_actor_x11_new (MetaWindow *window); void meta_surface_actor_x11_set_size (MetaSurfaceActorX11 *self, int width, int height); G_END_DECLS #endif /* __META_SURFACE_ACTOR_X11_H__ */ ukwm/src/compositor/meta-dnd-actor-private.h0000664000175000017500000000440513220600404020062 0ustar fengfeng/* -*- 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 "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 ()) #define META_DND_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_DND_ACTOR, MetaDnDActor)) #define META_DND_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_DND_ACTOR, MetaDnDActorClass)) #define META_IS_DND_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_DND_ACTOR)) #define META_IS_DND_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_DND_ACTOR)) #define META_DND_ACTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_DND_ACTOR, MetaDnDActorClass)) typedef struct _MetaDnDActor MetaDnDActor; typedef struct _MetaDnDActorClass MetaDnDActorClass; struct _MetaDnDActorClass { /*< private >*/ MetaFeedbackActorClass parent_class; }; struct _MetaDnDActor { MetaFeedbackActor parent; }; GType meta_dnd_actor_get_type (void); 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 */ ukwm/src/compositor/meta-surface-actor.c0000664000175000017500000002631313220600404017272 0ustar fengfeng/* -*- 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 * * A surface can be either a shaped texture, or a group of shaped texture, * used to draw the content of a window. */ #include #include "meta-surface-actor.h" #include #include #include "meta-cullable.h" #include "meta-shaped-texture-private.h" struct _MetaSurfaceActorPrivate { MetaShapedTexture *texture; cairo_region_t *input_region; /* Freeze/thaw accounting */ cairo_region_t *pending_damage; guint frozen : 1; }; static void cullable_iface_init (MetaCullableInterface *iface); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MetaSurfaceActor, meta_surface_actor, CLUTTER_TYPE_ACTOR, G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init)); enum { REPAINT_SCHEDULED, SIZE_CHANGED, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL]; static void meta_surface_actor_pick (ClutterActor *actor, const ClutterColor *color) { MetaSurfaceActor *self = META_SURFACE_ACTOR (actor); MetaSurfaceActorPrivate *priv = self->priv; 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) CLUTTER_ACTOR_CLASS (meta_surface_actor_parent_class)->pick (actor, color); else { int n_rects; float *rectangles; int i; CoglPipeline *pipeline; CoglContext *ctx; CoglFramebuffer *fb; CoglColor cogl_color; n_rects = cairo_region_num_rectangles (priv->input_region); rectangles = g_alloca (sizeof (float) * 4 * n_rects); for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t rect; int pos = i * 4; cairo_region_get_rectangle (priv->input_region, i, &rect); rectangles[pos + 0] = rect.x; rectangles[pos + 1] = rect.y; rectangles[pos + 2] = rect.x + rect.width; rectangles[pos + 3] = rect.y + rect.height; } ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); fb = cogl_get_draw_framebuffer (); cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha); pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_color (pipeline, &cogl_color); cogl_framebuffer_draw_rectangles (fb, pipeline, rectangles, n_rects); cogl_object_unref (pipeline); } clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) clutter_actor_paint (child); } static void meta_surface_actor_dispose (GObject *object) { MetaSurfaceActor *self = META_SURFACE_ACTOR (object); MetaSurfaceActorPrivate *priv = self->priv; g_clear_pointer (&priv->input_region, cairo_region_destroy); 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; 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); g_type_class_add_private (klass, sizeof (MetaSurfaceActorPrivate)); } static void meta_surface_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_surface_actor_reset_culling (MetaCullable *cullable) { meta_cullable_reset_culling_children (cullable); } static void cullable_iface_init (MetaCullableInterface *iface) { iface->cull_out = meta_surface_actor_cull_out; 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; priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, META_TYPE_SURFACE_ACTOR, MetaSurfaceActorPrivate); priv->texture = META_SHAPED_TEXTURE (meta_shaped_texture_new ()); g_signal_connect_object (priv->texture, "size-changed", G_CALLBACK (texture_size_changed), self, 0); clutter_actor_add_child (CLUTTER_ACTOR (self), CLUTTER_ACTOR (priv->texture)); } cairo_surface_t * meta_surface_actor_get_image (MetaSurfaceActor *self, cairo_rectangle_int_t *clip) { return meta_shaped_texture_get_image (self->priv->texture, clip); } MetaShapedTexture * meta_surface_actor_get_texture (MetaSurfaceActor *self) { return self->priv->texture; } static void meta_surface_actor_update_area (MetaSurfaceActor *self, int x, int y, int width, int height) { MetaSurfaceActorPrivate *priv = self->priv; if (meta_shaped_texture_update_area (priv->texture, x, y, width, height)) g_signal_emit (self, signals[REPAINT_SCHEDULED], 0); } gboolean meta_surface_actor_is_obscured (MetaSurfaceActor *self) { MetaSurfaceActorPrivate *priv = self->priv; return meta_shaped_texture_is_obscured (priv->texture); } void meta_surface_actor_set_input_region (MetaSurfaceActor *self, cairo_region_t *region) { MetaSurfaceActorPrivate *priv = self->priv; 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 = self->priv; meta_shaped_texture_set_opaque_region (priv->texture, region); } cairo_region_t * meta_surface_actor_get_opaque_region (MetaSurfaceActor *actor) { MetaSurfaceActorPrivate *priv = actor->priv; return meta_shaped_texture_get_opaque_region (priv->texture); } static gboolean is_frozen (MetaSurfaceActor *self) { MetaSurfaceActorPrivate *priv = self->priv; return priv->frozen; } void meta_surface_actor_process_damage (MetaSurfaceActor *self, int x, int y, int width, int height) { MetaSurfaceActorPrivate *priv = self->priv; if (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); if (meta_surface_actor_is_visible (self)) meta_surface_actor_update_area (self, x, y, width, height); } void meta_surface_actor_pre_paint (MetaSurfaceActor *self) { META_SURFACE_ACTOR_GET_CLASS (self)->pre_paint (self); } gboolean meta_surface_actor_is_argb32 (MetaSurfaceActor *self) { MetaShapedTexture *stex = meta_surface_actor_get_texture (self); CoglTexture *texture = meta_shaped_texture_get_texture (stex); /* If we don't have a texture, like during initialization, assume * that we're ARGB32. * * If we are unredirected and we have no texture assume that we are * not ARGB32 otherwise we wouldn't be unredirected in the first * place. This prevents us from continually redirecting and * unredirecting on every paint. */ if (!texture) return !meta_surface_actor_is_unredirected (self); 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 (); } } gboolean meta_surface_actor_is_visible (MetaSurfaceActor *self) { return META_SURFACE_ACTOR_GET_CLASS (self)->is_visible (self); } void meta_surface_actor_set_frozen (MetaSurfaceActor *self, gboolean frozen) { MetaSurfaceActorPrivate *priv = self->priv; 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_should_unredirect (MetaSurfaceActor *self) { return META_SURFACE_ACTOR_GET_CLASS (self)->should_unredirect (self); } void meta_surface_actor_set_unredirected (MetaSurfaceActor *self, gboolean unredirected) { META_SURFACE_ACTOR_GET_CLASS (self)->set_unredirected (self, unredirected); } gboolean meta_surface_actor_is_unredirected (MetaSurfaceActor *self) { return META_SURFACE_ACTOR_GET_CLASS (self)->is_unredirected (self); } MetaWindow * meta_surface_actor_get_window (MetaSurfaceActor *self) { return META_SURFACE_ACTOR_GET_CLASS (self)->get_window (self); } ukwm/src/compositor/clutter-utils.c0000664000175000017500000001537013220600404016431 0ustar fengfeng/* -*- 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 "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 (ClutterVertex *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; } /* Check if an actor is "untransformed" - which actually means transformed by * at most a integer-translation. The integer translation, if any, is returned. */ gboolean meta_actor_is_untransformed (ClutterActor *actor, int *x_origin, int *y_origin) { gfloat widthf, heightf; ClutterVertex verts[4]; clutter_actor_get_size (actor, &widthf, &heightf); clutter_actor_get_abs_allocation_vertices (actor, verts); return meta_actor_vertices_are_untransformed (verts, widthf, heightf, x_origin, y_origin); } /** * meta_actor_painting_untransformed: * @paint_width: the width of the painted area * @paint_height: the height of the painted area * @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 (int paint_width, int paint_height, int *x_origin, int *y_origin) { CoglMatrix modelview, projection, modelview_projection; ClutterVertex vertices[4]; float viewport[4]; int i; cogl_get_modelview_matrix (&modelview); cogl_get_projection_matrix (&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_get_viewport (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, paint_width, paint_height, x_origin, y_origin); } ukwm/src/compositor/meta-background-image.c0000664000175000017500000002313113220600404017726 0ustar fengfeng/* -*- 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 #include #include #include #include #include "cogl-utils.h" enum { LOADED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; struct _MetaBackgroundImageCache { GObject parent_instance; GHashTable *images; }; struct _MetaBackgroundImageCacheClass { GObjectClass parent_class; }; struct _MetaBackgroundImage { GObject parent_instance; GFile *file; MetaBackgroundImageCache *cache; gboolean in_cache; gboolean loaded; CoglTexture *texture; }; struct _MetaBackgroundImageClass { GObjectClass parent_class; }; 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; CoglError *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"); cogl_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; } ukwm/src/compositor/compositor-private.h0000664000175000017500000000475313220600404017467 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef META_COMPOSITOR_PRIVATE_H #define META_COMPOSITOR_PRIVATE_H #include #include #include #include "meta-plugin-manager.h" #include "meta-window-actor-private.h" #include struct _MetaCompositor { MetaDisplay *display; guint pre_paint_func_id; guint post_paint_func_id; gint64 server_time_query_time; gint64 server_time_offset; guint server_time_is_monotonic_time : 1; guint no_mipmaps : 1; ClutterActor *stage, *window_group, *top_window_group, *feedback_group; ClutterActor *background_actor; GList *windows; Window output; CoglContext *context; MetaWindowActor *top_window_actor; /* Used for unredirecting fullscreen windows */ guint disable_unredirect_count; MetaWindow *unredirected_window; gint switch_workspace_in_progress; MetaPluginManager *plugin_mgr; gboolean frame_has_updated_xsurfaces; gboolean have_x11_sync_object; }; /* Wait 2ms after vblank before starting to draw next frame */ #define META_SYNC_DELAY 2 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); gint64 meta_compositor_monotonic_time_to_server_time (MetaDisplay *display, gint64 monotonic_time); 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); #endif /* META_COMPOSITOR_PRIVATE_H */ ukwm/src/compositor/meta-window-group.c0000664000175000017500000001526313220600404017177 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include #define _ISOC99_SOURCE /* for roundf */ #include #include /* for gdk_rectangle_intersect() */ #include "clutter-utils.h" #include "compositor-private.h" #include "meta-window-actor-private.h" #include "meta-window-group.h" #include "window-private.h" #include "meta-cullable.h" struct _MetaWindowGroupClass { ClutterActorClass parent_class; }; struct _MetaWindowGroup { ClutterActor parent; MetaScreen *screen; }; 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) { cairo_region_t *clip_region; cairo_region_t *unobscured_region; cairo_rectangle_int_t visible_rect, clip_rect; int paint_x_origin, paint_y_origin; int screen_width, screen_height; MetaWindowGroup *window_group = META_WINDOW_GROUP (actor); ClutterActor *stage = clutter_actor_get_stage (actor); meta_screen_get_size (window_group->screen, &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)) { if (!meta_actor_painting_untransformed (screen_width, screen_height, &paint_x_origin, &paint_y_origin) || !meta_actor_is_untransformed (actor, NULL, NULL)) { CLUTTER_ACTOR_CLASS (meta_window_group_parent_class)->paint (actor); 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 from Clutter 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. */ clutter_stage_get_redraw_clip_bounds (CLUTTER_STAGE (stage), &clip_rect); clip_region = cairo_region_create_rectangle (&clip_rect); 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); CLUTTER_ACTOR_CLASS (meta_window_group_parent_class)->paint (actor); 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 (MetaScreen *screen) { MetaWindowGroup *window_group; window_group = g_object_new (META_TYPE_WINDOW_GROUP, NULL); window_group->screen = screen; return CLUTTER_ACTOR (window_group); } ukwm/src/compositor/meta-background.c0000664000175000017500000007303313220600404016654 0ustar fengfeng/* -*- 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 #include #include #include "meta-background-private.h" #include "cogl-utils.h" #include enum { CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; typedef struct _MetaBackgroundMonitor MetaBackgroundMonitor; struct _MetaBackgroundMonitor { gboolean dirty; CoglTexture *texture; CoglFramebuffer *fbo; }; struct _MetaBackgroundPrivate { MetaScreen *screen; MetaBackgroundMonitor *monitors; int n_monitors; GDesktopBackgroundStyle style; GDesktopBackgroundShading 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_SCREEN = 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) { MetaBackgroundPrivate *priv = self->priv; int i; for (i = 0; i < priv->n_monitors; i++) { MetaBackgroundMonitor *monitor = &priv->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) { MetaBackgroundPrivate *priv = self->priv; if (priv->color_texture != NULL) { cogl_object_unref (priv->color_texture); priv->color_texture = NULL; } } static void free_wallpaper_texture (MetaBackground *self) { MetaBackgroundPrivate *priv = self->priv; if (priv->wallpaper_texture != NULL) { cogl_object_unref (priv->wallpaper_texture); priv->wallpaper_texture = NULL; } priv->wallpaper_allocation_failed = FALSE; } static void on_monitors_changed (MetaScreen *screen, MetaBackground *self) { MetaBackgroundPrivate *priv = self->priv; free_fbos (self); g_free (priv->monitors); priv->monitors = NULL; priv->n_monitors = 0; if (priv->screen) { int i; priv->n_monitors = meta_screen_get_n_monitors (screen); priv->monitors = g_new0 (MetaBackgroundMonitor, priv->n_monitors); for (i = 0; i < priv->n_monitors; i++) priv->monitors[i].dirty = TRUE; } } static void set_screen (MetaBackground *self, MetaScreen *screen) { MetaBackgroundPrivate *priv = self->priv; if (priv->screen != NULL) { g_signal_handlers_disconnect_by_func (priv->screen, (gpointer)on_monitors_changed, self); } priv->screen = screen; if (priv->screen != NULL) { g_signal_connect (priv->screen, "monitors-changed", G_CALLBACK (on_monitors_changed), self); } on_monitors_changed (priv->screen, self); } static void meta_background_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { switch (prop_id) { case PROP_META_SCREEN: set_screen (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) { MetaBackgroundPrivate *priv = META_BACKGROUND (object)->priv; switch (prop_id) { case PROP_META_SCREEN: g_value_set_object (value, priv->screen); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static gboolean need_prerender (MetaBackground *self) { MetaBackgroundPrivate *priv = self->priv; CoglTexture *texture1 = priv->background_image1 ? meta_background_image_get_texture (priv->background_image1) : NULL; CoglTexture *texture2 = priv->background_image2 ? meta_background_image_get_texture (priv->background_image2) : NULL; if (texture1 == NULL && texture2 == NULL) return FALSE; if (texture2 == NULL && priv->style == G_DESKTOP_BACKGROUND_STYLE_WALLPAPER) return FALSE; return TRUE; } static void mark_changed (MetaBackground *self) { MetaBackgroundPrivate *priv = self->priv; int i; if (!need_prerender (self)) free_fbos (self); for (i = 0; i < priv->n_monitors; i++) priv->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) { if (!file_equal0 (*filep, file)) { g_clear_object (filep); if (*imagep) { g_signal_handlers_disconnect_by_func (*imagep, (gpointer)on_background_loaded, self); g_object_unref (*imagep); *imagep = NULL; } if (file) { MetaBackgroundImageCache *cache = meta_background_image_cache_get_default (); *filep = g_object_ref (file); *imagep = meta_background_image_cache_load (cache, file); g_signal_connect (*imagep, "loaded", G_CALLBACK (on_background_loaded), self); } } } static void meta_background_dispose (GObject *object) { MetaBackground *self = META_BACKGROUND (object); MetaBackgroundPrivate *priv = self->priv; free_color_texture (self); free_wallpaper_texture (self); set_file (self, &priv->file1, &priv->background_image1, NULL); set_file (self, &priv->file2, &priv->background_image2, NULL); set_screen (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); MetaBackgroundPrivate *priv = self->priv; G_OBJECT_CLASS (meta_background_parent_class)->constructed (object); g_signal_connect_object (meta_screen_get_display (priv->screen), "gl-video-memory-purged", G_CALLBACK (mark_changed), object, G_CONNECT_SWAPPED); } static void meta_background_class_init (MetaBackgroundClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *param_spec; g_type_class_add_private (klass, sizeof (MetaBackgroundPrivate)); 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-screen", "MetaScreen", "MetaScreen", META_TYPE_SCREEN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_META_SCREEN, param_spec); } static void meta_background_init (MetaBackground *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, META_TYPE_BACKGROUND, MetaBackgroundPrivate); 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, CoglTexture *texture, cairo_rectangle_int_t *texture_area) { MetaBackgroundPrivate *priv = self->priv; 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 (priv->style) { case G_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 G_DESKTOP_BACKGROUND_STYLE_WALLPAPER: meta_screen_get_size (priv->screen, &screen_width, &screen_height); /* Start off by centering a tile in the middle of the * total screen area. */ 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 G_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 G_DESKTOP_BACKGROUND_STYLE_SCALED: case G_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 ((priv->style == G_DESKTOP_BACKGROUND_STYLE_SCALED && (monitor_x_scale < monitor_y_scale)) || (priv->style == G_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 G_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_screen_get_size (priv->screen, &screen_width, &screen_height); /* unclipped texture area is whole screen */ image_area.width = screen_width; image_area.height = screen_height; /* 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) { MetaBackgroundPrivate *priv = self->priv; cairo_rectangle_int_t texture_area; gboolean bare_region_visible; get_texture_area (self, monitor_area, texture, &texture_area); switch (priv->style) { case G_DESKTOP_BACKGROUND_STYLE_STRETCHED: case G_DESKTOP_BACKGROUND_STYLE_WALLPAPER: case G_DESKTOP_BACKGROUND_STYLE_ZOOM: case G_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 G_DESKTOP_BACKGROUND_STYLE_CENTERED: case G_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 G_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) { MetaBackgroundPrivate *priv = self->priv; if (priv->color_texture == NULL) { ClutterBackend *backend = clutter_get_default_backend (); CoglContext *ctx = clutter_backend_get_cogl_context (backend); CoglError *error = NULL; uint8_t pixels[6]; int width, height; if (priv->shading_direction == G_DESKTOP_BACKGROUND_SHADING_SOLID) { width = 1; height = 1; pixels[0] = priv->color.red; pixels[1] = priv->color.green; pixels[2] = priv->color.blue; } else { switch (priv->shading_direction) { case G_DESKTOP_BACKGROUND_SHADING_VERTICAL: width = 1; height = 2; break; case G_DESKTOP_BACKGROUND_SHADING_HORIZONTAL: width = 2; height = 1; break; default: g_return_if_reached (); } pixels[0] = priv->color.red; pixels[1] = priv->color.green; pixels[2] = priv->color.blue; pixels[3] = priv->second_color.red; pixels[4] = priv->second_color.green; pixels[5] = priv->second_color.blue; } priv->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); cogl_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); } 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 (); } } static gboolean ensure_wallpaper_texture (MetaBackground *self, CoglTexture *texture) { MetaBackgroundPrivate *priv = self->priv; if (priv->wallpaper_texture == NULL && !priv->wallpaper_allocation_failed) { int width = cogl_texture_get_width (texture); int height = cogl_texture_get_height (texture); CoglOffscreen *offscreen; CoglFramebuffer *fbo; CoglError *catch_error = NULL; CoglPipeline *pipeline; priv->wallpaper_texture = meta_create_texture (width, height, COGL_TEXTURE_COMPONENTS_RGBA, META_TEXTURE_FLAGS_NONE); offscreen = cogl_offscreen_new_with_texture (priv->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. */ cogl_error_free (catch_error); cogl_object_unref (priv->wallpaper_texture); priv->wallpaper_texture = NULL; cogl_object_unref (fbo); priv->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, priv->color_texture); cogl_framebuffer_draw_rectangle (fbo, pipeline, 0, 0, width, height); cogl_object_unref (pipeline); } cogl_object_unref (fbo); } return priv->wallpaper_texture != NULL; } static CoglPipelineWrapMode get_wrap_mode (GDesktopBackgroundStyle style) { switch (style) { case G_DESKTOP_BACKGROUND_STYLE_WALLPAPER: return COGL_PIPELINE_WRAP_MODE_REPEAT; case G_DESKTOP_BACKGROUND_STYLE_NONE: case G_DESKTOP_BACKGROUND_STYLE_STRETCHED: case G_DESKTOP_BACKGROUND_STYLE_CENTERED: case G_DESKTOP_BACKGROUND_STYLE_SCALED: case G_DESKTOP_BACKGROUND_STYLE_ZOOM: case G_DESKTOP_BACKGROUND_STYLE_SPANNED: default: return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; } } CoglTexture * meta_background_get_texture (MetaBackground *self, int monitor_index, cairo_rectangle_int_t *texture_area, CoglPipelineWrapMode *wrap_mode) { MetaBackgroundPrivate *priv; MetaBackgroundMonitor *monitor; MetaRectangle geometry; cairo_rectangle_int_t monitor_area; CoglTexture *texture1, *texture2; g_return_val_if_fail (META_IS_BACKGROUND (self), NULL); priv = self->priv; g_return_val_if_fail (monitor_index >= 0 && monitor_index < priv->n_monitors, NULL); monitor = &priv->monitors[monitor_index]; meta_screen_get_monitor_geometry (priv->screen, monitor_index, &geometry); monitor_area.x = geometry.x; monitor_area.y = geometry.y; monitor_area.width = geometry.width; monitor_area.height = geometry.height; texture1 = priv->background_image1 ? meta_background_image_get_texture (priv->background_image1) : NULL; texture2 = priv->background_image2 ? meta_background_image_get_texture (priv->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 priv->color_texture; } if (texture2 == NULL && priv->style == G_DESKTOP_BACKGROUND_STYLE_WALLPAPER && priv->shading_direction == G_DESKTOP_BACKGROUND_SHADING_SOLID && ensure_wallpaper_texture (self, texture1)) { if (texture_area) get_texture_area (self, &monitor_area, priv->wallpaper_texture, texture_area); if (wrap_mode) *wrap_mode = COGL_PIPELINE_WRAP_MODE_REPEAT; return priv->wallpaper_texture; } if (monitor->dirty) { CoglError *catch_error = NULL; gboolean bare_region_visible = FALSE; if (monitor->texture == NULL) { CoglOffscreen *offscreen; monitor->texture = meta_create_texture (monitor_area.width, monitor_area.height, COGL_TEXTURE_COMPONENTS_RGBA, META_TEXTURE_FLAGS_NONE); offscreen = cogl_offscreen_new_with_texture (monitor->texture); monitor->fbo = COGL_FRAMEBUFFER (offscreen); } 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; cogl_error_free (catch_error); return NULL; } cogl_framebuffer_orthographic (monitor->fbo, 0, 0, monitor_area.width, monitor_area.height, -1., 1.); if (texture2 != NULL && priv->blend_factor != 0.0) { CoglPipeline *pipeline = create_pipeline (PIPELINE_REPLACE); cogl_pipeline_set_color4f (pipeline, priv->blend_factor, priv->blend_factor, priv->blend_factor, priv->blend_factor); cogl_pipeline_set_layer_texture (pipeline, 0, texture2); cogl_pipeline_set_layer_wrap_mode (pipeline, 0, get_wrap_mode (priv->style)); bare_region_visible = draw_texture (self, monitor->fbo, pipeline, texture2, &monitor_area); 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 && priv->blend_factor != 1.0) { CoglPipeline *pipeline = create_pipeline (PIPELINE_ADD); cogl_pipeline_set_color4f (pipeline, (1 - priv->blend_factor), (1 - priv->blend_factor), (1 - priv->blend_factor), (1 - priv->blend_factor));; cogl_pipeline_set_layer_texture (pipeline, 0, texture1); cogl_pipeline_set_layer_wrap_mode (pipeline, 0, get_wrap_mode (priv->style)); bare_region_visible = bare_region_visible || draw_texture (self, monitor->fbo, pipeline, texture1, &monitor_area); 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, priv->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 (&monitor_area, texture_area); if (wrap_mode) *wrap_mode = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; return monitor->texture; } MetaBackground * meta_background_new (MetaScreen *screen) { return g_object_new (META_TYPE_BACKGROUND, "meta-screen", screen, 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, G_DESKTOP_BACKGROUND_SHADING_SOLID, color, &dummy); } void meta_background_set_gradient (MetaBackground *self, GDesktopBackgroundShading shading_direction, ClutterColor *color, ClutterColor *second_color) { MetaBackgroundPrivate *priv; g_return_if_fail (META_IS_BACKGROUND (self)); g_return_if_fail (color != NULL); g_return_if_fail (second_color != NULL); priv = self->priv; priv->shading_direction = shading_direction; priv->color = *color; priv->second_color = *second_color; free_color_texture (self); free_wallpaper_texture (self); mark_changed (self); } void meta_background_set_file (MetaBackground *self, GFile *file, GDesktopBackgroundStyle 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, GDesktopBackgroundStyle style) { MetaBackgroundPrivate *priv; g_return_if_fail (META_IS_BACKGROUND (self)); g_return_if_fail (blend_factor >= 0.0 && blend_factor <= 1.0); priv = self->priv; set_file (self, &priv->file1, &priv->background_image1, file1); set_file (self, &priv->file2, &priv->background_image2, file2); priv->blend_factor = blend_factor; priv->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); } ukwm/src/compositor/meta-plugin.c0000664000175000017500000001452713220600404016036 0ustar fengfeng/* -*- 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 #include "meta-plugin-manager.h" #include #include #include #include #include #include #include #include #include "compositor-private.h" #include "meta-window-actor-private.h" #include "meta-monitor-manager-private.h" G_DEFINE_ABSTRACT_TYPE (MetaPlugin, meta_plugin, G_TYPE_OBJECT); #define META_PLUGIN_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), META_TYPE_PLUGIN, MetaPluginPrivate)) struct _MetaPluginPrivate { MetaCompositor *compositor; }; static void meta_plugin_class_init (MetaPluginClass *klass) { g_type_class_add_private (klass, sizeof (MetaPluginPrivate)); } static void meta_plugin_init (MetaPlugin *self) { self->priv = META_PLUGIN_GET_PRIVATE (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 (plugin)->priv; 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 (plugin)->priv; 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 (plugin)->priv; meta_end_modal_for_plugin (priv->compositor, plugin, timestamp); } /** * meta_plugin_get_screen: * @plugin: a #MetaPlugin * * Gets the #MetaScreen corresponding to a plugin. * * Return value: (transfer none): the #MetaScreen for the plugin */ MetaScreen * meta_plugin_get_screen (MetaPlugin *plugin) { MetaPluginPrivate *priv = META_PLUGIN (plugin)->priv; return priv->compositor->display->screen; } void _meta_plugin_set_compositor (MetaPlugin *plugin, MetaCompositor *compositor) { MetaPluginPrivate *priv = META_PLUGIN (plugin)->priv; 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); } ukwm/src/compositor/plugins/0000775000175000017500000000000013254647345015146 5ustar fengfengukwm/src/compositor/plugins/ukui_plugin.h0000664000175000017500000000165213247121716017645 0ustar fengfeng#ifndef UKUI_PLUGIN_H #define UKUI_PLUGIN_H #include #define UKUI_PLUGIN_BUS G_BUS_TYPE_SESSION #define UKUI_PLUGIN_BUS_NAME "org.ukui.ukwm.UkwmPlugin" #define UKUI_PLUGIN_OBJECT_PATH "/org/ukui/ukwm/UkwmPlugin" #define PREVIEW_WIDTH 168 #define PREVIEW_HEIGHT 128 #define SPACE_WIDTH 8 #define SPACE_HEIGHT 8 #define THUMBNAIL_WIDTH (PREVIEW_WIDTH - SPACE_WIDTH) #define THUMBNAIL_HEIGHT (PREVIEW_HEIGHT - SPACE_HEIGHT) #define ICON_WIDTH 48 #define ICON_HEIGHT 48 #define ATP_COUNT 128 typedef struct _alt_tab_item { char * title_name; unsigned long xid; int x; int y; int width; int height; void * icon; } alt_tab_item; #define PATH_MAX_LEN 1024 #define PID_STRING_LEN 64 #define TAB_LIST_IMAGE_FILE "ukwm-tab-list.image" #define WORKSPACE_IMAGE_FILE "ukwm-workspace.image" #define PROGRAM_NAME "ukui-window-switch" #endif // UKUI_PLUGIN_H ukwm/src/compositor/plugins/org.ukui.ukwm.UkwmPlugin.xml0000664000175000017500000000102413233760065022503 0ustar fengfeng Get Device List Activate Window By Tab List Index ukwm/src/compositor/plugins/Makefile.am0000664000175000017500000000362713233760116017177 0ustar fengfengNULL= pkglibdir = $(UKWM_PLUGIN_DIR) AM_CPPFLAGS = \ $(UKWM_CFLAGS) \ -I$(top_builddir)/src \ -I$(top_srcdir)/src \ -I$(top_srcdir)/src/core \ -I$(top_srcdir)/src/ui \ -I$(top_srcdir)/src/backends \ -I$(top_srcdir)/cogl \ -I$(top_builddir)/cogl \ -I$(top_builddir)/cogl/cogl \ -I$(top_srcdir)/clutter \ -I$(top_builddir)/clutter \ -I$(top_builddir)/clutter/clutter \ -DUKWM_LIBEXECDIR=\"$(libexecdir)\" \ -DUKWM_LOCALEDIR=\"$(localedir)\" \ -DUKWM_PKGDATADIR=\"$(pkgdatadir)\" \ -DUKWM_DATADIR=\"$(datadir)\" \ -DG_LOG_DOMAIN=\"ukwm\" \ -DSN_API_NOT_YET_FROZEN=1 \ -DUKWM_PLUGIN_DIR=\"$(UKWM_PLUGIN_DIR)\" default_la_CFLAGS = -fPIC default_la_SOURCES = default.c \ ukui_plugin.h \ ukui_plugin_generated.c \ $(NULL) default_la_LDFLAGS = -module -avoid-version -no-undefined default_la_LIBADD = $(CLUTTER_LIBS) DESC_XML = org.ukui.ukwm.UkwmPlugin.xml GENERATED_CH = ukui_plugin_generated default.c: ukui_plugin_generated.h ukui_plugin_generated.c ukui_plugin_generated.h: $(DESC_XML) Makefile gdbus-codegen --interface org.ukui.ukwm --generate-c-code $(GENERATED_CH) $(DESC_XML) default_includedir=$(includedir)/ukwm/compositor/plugins/ default_include_HEADERS = ukui_plugin.h pkglib_LTLIBRARIES = default.la ukui_plugin_xmldir = $(datadir)/ukui/plugin/ ukui_plugin_xml_DATA = $(DESC_XML) # post-install hook to remove the .la and .a files we are not interested in # (There is no way to stop libtool generating static libs locally, and we # cannot do this globally because of libukwm-private.so). install-exec-hook: -rm -f $(DESTDIR)$(pkglibdir)/*.a -rm -f $(DESTDIR)$(pkglibdir)/*.la # Since we removed the .la file, 'make uninstall' doesn't work properly, # since it counts on libtool to remove the .la files, so just kill the # .so file manually. uninstall-local: -rm -f $(DESTDIR)$(pkglibdir)/default.so ukwm/src/compositor/plugins/default.c0000664000175000017500000010565613254647345016753 0ustar fengfeng/* -*- 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 #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 #include #define DESTROY_TIMEOUT 100 #define MINIMIZE_TIMEOUT 250 #define MAP_TIMEOUT 250 #define SWITCH_TIMEOUT 500 static UkwmPlugin *pSkeleton = NULL; MetaPlugin * global_plugin; MetaScreen * global_screen; MetaDisplay * global_display; GList * global_tab_list = NULL; alt_tab_item ati_list[ATP_COUNT]; #define ACTOR_DATA_KEY "MCCP-Default-actor-data" #define SCREEN_TILE_PREVIEW_DATA_KEY "MCCP-Default-screen-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)) #define META_DEFAULT_PLUGIN_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), META_TYPE_DEFAULT_PLUGIN, MetaDefaultPluginPrivate)) typedef struct _MetaDefaultPlugin MetaDefaultPlugin; typedef struct _MetaDefaultPluginClass MetaDefaultPluginClass; typedef struct _MetaDefaultPluginPrivate MetaDefaultPluginPrivate; static int uid; static char pid_file[PATH_MAX_LEN] = {0}; static char tab_list_image_file[PATH_MAX_LEN] = {0}; //static char workspace_image_file[PATH_MAX_LEN] = {0}; struct _MetaDefaultPlugin { MetaPlugin parent; MetaDefaultPluginPrivate *priv; }; struct _MetaDefaultPluginClass { MetaPluginClass parent_class; }; static GQuark actor_data_quark = 0; static GQuark screen_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); META_PLUGIN_DECLARE(MetaDefaultPlugin, meta_default_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; }; /* * 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 _ScreenTilePreview { ClutterActor *actor; GdkRGBA *preview_color; MetaRectangle tile_rect; } ScreenTilePreview; 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; g_type_class_add_private (gobject_class, sizeof (MetaDefaultPluginPrivate)); } static void meta_default_plugin_init (MetaDefaultPlugin *self) { MetaDefaultPluginPrivate *priv; self->priv = priv = META_DEFAULT_PLUGIN_GET_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; MetaScreen *screen = meta_plugin_get_screen (plugin); GList *l = meta_get_window_actors (screen); 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 (MetaScreen *screen, MetaPlugin *plugin) { MetaDefaultPlugin *self = META_DEFAULT_PLUGIN (plugin); int i, n; GRand *rand = g_rand_new_with_seed (123456); clutter_actor_destroy_all_children (self->priv->background_group); n = meta_screen_get_n_monitors (screen); for (i = 0; i < n; i++) { MetaRectangle rect; ClutterActor *background_actor; MetaBackground *background; ClutterColor color; meta_screen_get_monitor_geometry (screen, i, &rect); background_actor = meta_background_actor_new (screen, i); clutter_actor_set_position (background_actor, rect.x, rect.y); clutter_actor_set_size (background_actor, rect.width, rect.height); /* UKUI: set init color black as default*/ clutter_color_init (&color, 0, 0, 0, 255); background = meta_background_new (screen); 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 gboolean ukwm_plugin_get_alt_tab_list(MetaPlugin *object, GDBusMethodInvocation *invocation) { int count = 0; MetaScreen * screen; MetaDisplay * display; MetaTabList type = META_TAB_LIST_NORMAL; GList * l = NULL; GVariant * gva; GVariantBuilder* _builder = g_variant_builder_new(G_VARIANT_TYPE("av")); screen = meta_plugin_get_screen(global_plugin); display = meta_screen_get_display(screen); if (global_tab_list != NULL) g_list_free(global_tab_list); global_tab_list = meta_display_get_tab_list (display, type, NULL); l = global_tab_list; int offset_x = 0; int offset_y = 0; while (l != NULL) { if (count >= ATP_COUNT) break; MetaWindow * win = l->data; int icon_width = cairo_image_surface_get_width(win->icon); int icon_height = cairo_image_surface_get_height(win->icon); ati_list[count].title_name = win->title; ati_list[count].xid = (unsigned long)win->xwindow; ati_list[count].width = icon_width; ati_list[count].height = icon_height; ati_list[count].x = offset_x; ati_list[count].y = offset_y; ati_list[count].icon = (void *)gdk_pixbuf_get_from_surface(win->icon, 0, 0, icon_width, icon_height); offset_x += ati_list[count].width; GVariant * _item = g_variant_new("(siiiii)", ati_list[count].title_name, ati_list[count].xid, ati_list[count].width, ati_list[count].height, ati_list[count].x, ati_list[count].y); g_variant_builder_add(_builder, "v", _item); l = l->next; count += 1; } gva = g_variant_builder_end(_builder); g_variant_builder_unref(_builder); int i = 0; int max_width = 0; int max_height = 0; for (i = 0; i < count; i++) { if (ati_list[i].x + ati_list[i].width > max_width) max_width = ati_list[i].x + ati_list[i].width; if (ati_list[i].y + ati_list[i].height > max_height) max_height = ati_list[i].y + ati_list[i].height; } GdkPixbuf * alt_tab_list_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, max_width, max_height); memset(gdk_pixbuf_get_pixels(alt_tab_list_pixbuf), 0, max_width * max_height * 4); for (i = 0; i < count; i++) { gdk_pixbuf_copy_area((GdkPixbuf *)ati_list[i].icon, 0, 0, ati_list[i].width, ati_list[i].height, alt_tab_list_pixbuf, ati_list[i].x, ati_list[i].y); g_object_unref((GdkPixbuf *)ati_list[i].icon); } gdk_pixbuf_save(alt_tab_list_pixbuf, tab_list_image_file, "png", NULL, NULL); chmod(tab_list_image_file, 0660); g_object_unref(alt_tab_list_pixbuf); ukwm_plugin_complete_get_alt_tab_list(object, invocation, count, gva); return true; } static gboolean ukwm_plugin_activate_window_by_tab_list_index(MetaPlugin *object, GDBusMethodInvocation *invocation, int index) { MetaScreen * screen; MetaDisplay * display; MetaWindow * window; MetaTabList type = META_TAB_LIST_NORMAL; screen = meta_plugin_get_screen(global_plugin); display = meta_screen_get_display(screen); if (global_tab_list == NULL) { ukwm_plugin_complete_activate_window_by_tab_list_index(object, invocation); return true; } index = index % g_list_length(global_tab_list); window = g_list_nth_data (global_tab_list, index); GList * now_tab_list = meta_display_get_tab_list (display, type, NULL); GList * window_exist = g_list_find(now_tab_list, window); if (window_exist == NULL) { ukwm_plugin_complete_activate_window_by_tab_list_index(object, invocation); return true; } struct timeval tv1, tv2; // struct sysinfo info; guint32 timestamp = 0; /* if (sysinfo(&info)) { fprintf(stderr, "Failed to get sysinfo, errno:%u, reason:%s\n", errno, strerror(errno)); timestamp = 0; } else timestamp = info.uptime * 1000; */ if (window) meta_window_activate (window, timestamp); g_list_free(now_tab_list); g_list_free(global_tab_list); global_tab_list = NULL; ukwm_plugin_complete_activate_window_by_tab_list_index(object, invocation); return true; } static void bus_acquired_cb(GDBusConnection *connection, const gchar *bus_name, gpointer user_data) { GError *pError = NULL; /** Second step: Try to get a connection to the given bus. */ pSkeleton = ukwm_plugin_skeleton_new(); /** Third step: Attach to dbus signals. */ g_signal_connect(pSkeleton, "handle-get-alt-tab-list", G_CALLBACK(ukwm_plugin_get_alt_tab_list), NULL); g_signal_connect(pSkeleton, "handle-activate-window-by-tab-list-index", G_CALLBACK(ukwm_plugin_activate_window_by_tab_list_index), NULL); /** Fourth step: Export interface skeleton. */ (void) g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(pSkeleton), connection, UKUI_PLUGIN_OBJECT_PATH, &pError); if(pError != NULL) { g_print("Error: Failed to export object. Reason: %s.\n", pError->message); g_error_free(pError); } } static void name_acquired_cb(GDBusConnection *connection, const gchar *bus_name, gpointer user_data) { g_print("name_acquired_cb call, Acquired bus name: %s.\n", UKUI_PLUGIN_BUS_NAME); } static void name_lost_cb(GDBusConnection *connection, const gchar *bus_name, gpointer user_data) { if(connection == NULL) { g_print("name_lost_cb call, Error: Failed to connect to dbus.\n"); } else { g_print("name_lost_cb call, Error: Failed to obtain bus name: %s.\n", UKUI_PLUGIN_BUS_NAME); } } bool InitUkwmPluginDBusCommServer(void) { bool bRet = TRUE; g_print("InitDBusCommunicationServer: Server started.\n"); /** first step: connect to dbus */ (void)g_bus_own_name(UKUI_PLUGIN_BUS, UKUI_PLUGIN_BUS_NAME, G_BUS_NAME_OWNER_FLAGS_REPLACE|G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, &bus_acquired_cb, &name_acquired_cb, &name_lost_cb, NULL, NULL); return bRet; } void ukui_window_switch_monitor(void) { sleep(5); int lock_ret = -1; int pid_file_fd = open(pid_file, O_CREAT | O_TRUNC | O_RDWR, 0666); if (pid_file_fd < 0) { fprintf(stderr, "Can not open pid file[%s], %s\n", pid_file, strerror(pid_file_fd)); return ; } while (1) { sleep(2); int pid_file_fd = open(pid_file, O_CREAT | O_TRUNC | O_RDWR, 0666); if (pid_file_fd < 0) { fprintf(stderr, "Can not open pid file[%s], %s\n", pid_file, strerror(pid_file_fd)); break; } else { int flags = fcntl(pid_file_fd, F_GETFD); fcntl(pid_file_fd, F_SETFD, flags | FD_CLOEXEC ); lock_ret = flock(pid_file_fd, LOCK_EX | LOCK_NB); if (lock_ret == 0) { printf("ukui-window-switch is not running...\n"); flock(pid_file_fd, LOCK_UN); pid_t kws_pid; kws_pid = fork(); if (kws_pid == 0) { char bin_file[PATH_MAX_LEN] = {0}; snprintf(bin_file, PATH_MAX_LEN, "/usr/bin/%s", PROGRAM_NAME); if (access(bin_file, F_OK | R_OK | X_OK) == 0) { int err; err = execlp(PROGRAM_NAME, PROGRAM_NAME, NULL); fprintf(stderr, "Can not exec %s: %s\n", PROGRAM_NAME, strerror(err)); } exit(0); } waitpid(-1, NULL, WNOHANG); } close(pid_file_fd); } } } static void start (MetaPlugin *plugin) { MetaDefaultPlugin *self = META_DEFAULT_PLUGIN (plugin); MetaScreen *screen = meta_plugin_get_screen (plugin); self->priv->background_group = meta_background_group_new (); clutter_actor_insert_child_below (meta_get_window_group_for_screen (screen), self->priv->background_group, NULL); g_signal_connect (screen, "monitors-changed", G_CALLBACK (on_monitors_changed), plugin); on_monitors_changed (screen, plugin); clutter_actor_show (meta_get_stage_for_screen (screen)); uid = getuid(); snprintf(pid_file, PATH_MAX_LEN, "/run/user/%d/%s.pid", uid, PROGRAM_NAME); snprintf(tab_list_image_file, PATH_MAX_LEN, "/run/user/%d/%s", uid, TAB_LIST_IMAGE_FILE); global_plugin = plugin; InitUkwmPluginDBusCommServer(); int err; pthread_t monitor_thread; err = pthread_create(&monitor_thread, NULL, ukui_window_switch_monitor, NULL); if (err != 0) fprintf(stderr, "Can't create ukui-window-switch monitor: %s\n", strerror(err)); } static void switch_workspace (MetaPlugin *plugin, gint from, gint to, MetaMotionDirection direction) { MetaScreen *screen; 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; screen = meta_plugin_get_screen (plugin); stage = meta_get_stage_for_screen (screen); meta_screen_get_size (screen, &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 (screen)); 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); 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) { EffectCompleteData *data = g_new0 (EffectCompleteData, 1); ActorPrivate *apriv = get_actor_private (window_actor); apriv->tml_minimize = 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); 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); 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); apriv->tml_destroy = actor_animate (actor, CLUTTER_EASE_OUT_QUAD, DESTROY_TIMEOUT, "opacity", 0, "scale-x", 0.8, "scale-y", 0.8, NULL); 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_screen_tile_preview (gpointer data) { ScreenTilePreview *preview = data; if (G_LIKELY (preview != NULL)) { clutter_actor_destroy (preview->actor); g_slice_free (ScreenTilePreview, preview); } } static ScreenTilePreview * get_screen_tile_preview (MetaScreen *screen) { ScreenTilePreview *preview = g_object_get_qdata (G_OBJECT (screen), screen_tile_preview_data_quark); if (G_UNLIKELY (screen_tile_preview_data_quark == 0)) screen_tile_preview_data_quark = g_quark_from_static_string (SCREEN_TILE_PREVIEW_DATA_KEY); if (G_UNLIKELY (!preview)) { preview = g_slice_new0 (ScreenTilePreview); 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_screen (screen), preview->actor); g_object_set_qdata_full (G_OBJECT (screen), screen_tile_preview_data_quark, preview, free_screen_tile_preview); } return preview; } static void show_tile_preview (MetaPlugin *plugin, MetaWindow *window, MetaRectangle *tile_rect, int tile_monitor_number) { MetaScreen *screen = meta_plugin_get_screen (plugin); ScreenTilePreview *preview = get_screen_tile_preview (screen); 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) { MetaScreen *screen = meta_plugin_get_screen (plugin); ScreenTilePreview *preview = get_screen_tile_preview (screen); clutter_actor_hide (preview->actor); } static void kill_switch_workspace (MetaPlugin *plugin) { MetaDefaultPluginPrivate *priv = META_DEFAULT_PLUGIN (plugin)->priv; if (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); } } static void kill_window_effects (MetaPlugin *plugin, MetaWindowActor *window_actor) { ActorPrivate *apriv; apriv = get_actor_private (window_actor); if (apriv->tml_minimize) { clutter_timeline_stop (apriv->tml_minimize); g_signal_emit_by_name (apriv->tml_minimize, "completed", NULL); } if (apriv->tml_map) { clutter_timeline_stop (apriv->tml_map); g_signal_emit_by_name (apriv->tml_map, "completed", NULL); } if (apriv->tml_destroy) { clutter_timeline_stop (apriv->tml_destroy); g_signal_emit_by_name (apriv->tml_destroy, "completed", NULL); } } 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", "preferences-desktop-display", 0, NULL, NULL); g_child_watch_add (pid, on_dialog_closed, plugin); } ukwm/src/compositor/meta-texture-rectangle.h0000664000175000017500000000206113220600404020175 0ustar fengfeng/* * texture rectangle * * A small utility function to help create a rectangle texture * * Authored By Neil Roberts * * Copyright (C) 2011 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_TEXTURE_RECTANGLE_H__ #define __META_TEXTURE_RECTANGLE_H__ #include G_BEGIN_DECLS gboolean meta_texture_rectangle_check (CoglTexture *texture); G_END_DECLS #endif /* __META_TEXTURE_RECTANGLE_H__ */ ukwm/src/compositor/meta-background-actor.c0000664000175000017500000010373513220600404017765 0ustar fengfeng/* -*- 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 #include #include "cogl-utils.h" #include "clutter-utils.h" #include #include "meta-background-actor-private.h" #include "meta-background-private.h" #include "meta-cullable.h" enum { PROP_META_SCREEN = 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_FRAGMENT_SHADER_DECLARATIONS \ "uniform float vignette_sharpness;\n" \ "varying vec2 position;\n" \ #define VIGNETTE_FRAGMENT_SHADER_CODE \ "float t = 2.0 * 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" \ typedef struct _MetaBackgroundLayer MetaBackgroundLayer; typedef enum { PIPELINE_VIGNETTE = (1 << 0), PIPELINE_BLEND = (1 << 1), PIPELINE_GRADIENT = (1 << 2), } PipelineFlags; struct _MetaBackgroundActorPrivate { MetaScreen *screen; 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; }; 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) { MetaBackgroundActorPrivate *priv = self->priv; g_clear_pointer (&priv->clip_region, (GDestroyNotify) cairo_region_destroy); if (clip_region) priv->clip_region = cairo_region_copy (clip_region); } static void meta_background_actor_dispose (GObject *object) { MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object); MetaBackgroundActorPrivate *priv = self->priv; set_clip_region (self, NULL); meta_background_actor_set_background (self, NULL); if (priv->pipeline) { cogl_object_unref (priv->pipeline); priv->pipeline = NULL; } G_OBJECT_CLASS (meta_background_actor_parent_class)->dispose (object); } static void get_preferred_size (MetaBackgroundActor *self, gfloat *width, gfloat *height) { MetaBackgroundActorPrivate *priv = META_BACKGROUND_ACTOR (self)->priv; MetaRectangle monitor_geometry; meta_screen_get_monitor_geometry (priv->screen, priv->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, cairo_rectangle_int_t *actor_pixel_rect) { MetaBackgroundActorPrivate *priv = self->priv; PipelineFlags pipeline_flags = 0; guint8 opacity; float color_component; CoglPipelineFilter filter; opacity = clutter_actor_get_paint_opacity (CLUTTER_ACTOR (self)); if (opacity < 255) pipeline_flags |= PIPELINE_BLEND; if (priv->vignette && clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) pipeline_flags |= PIPELINE_VIGNETTE; if (priv->gradient && clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) pipeline_flags |= PIPELINE_GRADIENT; if (priv->pipeline && pipeline_flags != priv->pipeline_flags) { cogl_object_unref (priv->pipeline); priv->pipeline = NULL; } if (priv->pipeline == NULL) { priv->pipeline_flags = pipeline_flags; priv->pipeline = make_pipeline (pipeline_flags); priv->changed = CHANGED_ALL; } if ((priv->changed & CHANGED_BACKGROUND) != 0) { CoglPipelineWrapMode wrap_mode; CoglTexture *texture = meta_background_get_texture (priv->background, priv->monitor, &priv->texture_area, &wrap_mode); priv->force_bilinear = texture && (priv->texture_area.width != (int)cogl_texture_get_width (texture) || priv->texture_area.height != (int)cogl_texture_get_height (texture)); cogl_pipeline_set_layer_texture (priv->pipeline, 0, texture); cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0, wrap_mode); priv->changed &= ~CHANGED_BACKGROUND; } if ((priv->changed & CHANGED_VIGNETTE_PARAMETERS) != 0) { cogl_pipeline_set_uniform_1f (priv->pipeline, cogl_pipeline_get_uniform_location (priv->pipeline, "vignette_sharpness"), priv->vignette_sharpness); priv->changed &= ~CHANGED_VIGNETTE_PARAMETERS; } if ((priv->changed & CHANGED_GRADIENT_PARAMETERS) != 0) { MetaRectangle monitor_geometry; float gradient_height_perc; meta_screen_get_monitor_geometry (priv->screen, priv->monitor, &monitor_geometry); gradient_height_perc = MAX (0.0001, priv->gradient_height / (float)monitor_geometry.height); cogl_pipeline_set_uniform_1f (priv->pipeline, cogl_pipeline_get_uniform_location (priv->pipeline, "gradient_height_perc"), gradient_height_perc); cogl_pipeline_set_uniform_1f (priv->pipeline, cogl_pipeline_get_uniform_location (priv->pipeline, "gradient_max_darkness"), priv->gradient_max_darkness); priv->changed &= ~CHANGED_GRADIENT_PARAMETERS; } if (priv->vignette) { color_component = priv->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 * priv->vignette_sharpness); } } else color_component = opacity / 255.; cogl_pipeline_set_color4f (priv->pipeline, color_component, color_component, color_component, opacity / 255.); if (!priv->force_bilinear && meta_actor_painting_untransformed (actor_pixel_rect->width, actor_pixel_rect->height, NULL, NULL)) filter = COGL_PIPELINE_FILTER_NEAREST; else filter = COGL_PIPELINE_FILTER_LINEAR; cogl_pipeline_set_layer_filters (priv->pipeline, 0, filter, filter); } static void set_glsl_parameters (MetaBackgroundActor *self, cairo_rectangle_int_t *actor_pixel_rect) { MetaBackgroundActorPrivate *priv = self->priv; 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] = priv->texture_area.width / (float)actor_pixel_rect->width; scale[1] = priv->texture_area.height / (float)actor_pixel_rect->height; offset[0] = priv->texture_area.x / (float)actor_pixel_rect->width - 0.5; offset[1] = priv->texture_area.y / (float)actor_pixel_rect->height - 0.5; cogl_pipeline_set_uniform_float (priv->pipeline, cogl_pipeline_get_uniform_location (priv->pipeline, "scale"), 2, 1, scale); cogl_pipeline_set_uniform_float (priv->pipeline, cogl_pipeline_get_uniform_location (priv->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) { MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor); MetaBackgroundActorPrivate *priv = self->priv; ClutterActorBox actor_box; cairo_rectangle_int_t actor_pixel_rect; CoglFramebuffer *fb; int i; if ((priv->clip_region && cairo_region_is_empty (priv->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, &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 = cogl_get_draw_framebuffer (); /* Now figure out what to actually paint. */ if (priv->clip_region != NULL) { int n_rects = cairo_region_num_rectangles (priv->clip_region); if (n_rects <= MAX_RECTS) { for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (priv->clip_region, i, &rect); if (!gdk_rectangle_intersect (&actor_pixel_rect, &rect, &rect)) continue; paint_clipped_rectangle (fb, priv->pipeline, &rect, &priv->texture_area); } return; } } paint_clipped_rectangle (fb, priv->pipeline, &actor_pixel_rect, &priv->texture_area); } static void meta_background_actor_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object); MetaBackgroundActorPrivate *priv = self->priv; switch (prop_id) { case PROP_META_SCREEN: priv->screen = 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), priv->gradient_height, priv->gradient_max_darkness); break; case PROP_GRADIENT_HEIGHT: meta_background_actor_set_gradient (self, priv->gradient, g_value_get_int (value), priv->gradient_max_darkness); break; case PROP_GRADIENT_MAX_DARKNESS: meta_background_actor_set_gradient (self, priv->gradient, priv->gradient_height, g_value_get_double (value)); break; case PROP_VIGNETTE: meta_background_actor_set_vignette (self, g_value_get_boolean (value), priv->vignette_brightness, priv->vignette_sharpness); break; case PROP_VIGNETTE_SHARPNESS: meta_background_actor_set_vignette (self, priv->vignette, priv->vignette_brightness, g_value_get_double (value)); break; case PROP_VIGNETTE_BRIGHTNESS: meta_background_actor_set_vignette (self, priv->vignette, g_value_get_double (value), priv->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) { MetaBackgroundActorPrivate *priv = META_BACKGROUND_ACTOR (object)->priv; switch (prop_id) { case PROP_META_SCREEN: g_value_set_object (value, priv->screen); break; case PROP_MONITOR: g_value_set_int (value, priv->monitor); break; case PROP_BACKGROUND: g_value_set_object (value, priv->background); break; case PROP_GRADIENT: g_value_set_boolean (value, priv->gradient); break; case PROP_GRADIENT_HEIGHT: g_value_set_int (value, priv->gradient_height); break; case PROP_GRADIENT_MAX_DARKNESS: g_value_set_double (value, priv->gradient_max_darkness); break; case PROP_VIGNETTE: g_value_set_boolean (value, priv->vignette); break; case PROP_VIGNETTE_BRIGHTNESS: g_value_set_double (value, priv->vignette_brightness); break; case PROP_VIGNETTE_SHARPNESS: g_value_set_double (value, priv->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; g_type_class_add_private (klass, sizeof (MetaBackgroundActorPrivate)); 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-screen", "MetaScreen", "MetaScreen", META_TYPE_SCREEN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_META_SCREEN, 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) { MetaBackgroundActorPrivate *priv; priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, META_TYPE_BACKGROUND_ACTOR, MetaBackgroundActorPrivate); priv->gradient = FALSE; priv->gradient_height = 0; priv->gradient_max_darkness = 0.0; priv->vignette = FALSE; priv->vignette_brightness = 1.0; priv->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 (MetaScreen *screen, int monitor) { MetaBackgroundActor *self; self = g_object_new (META_TYPE_BACKGROUND_ACTOR, "meta-screen", screen, "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_clip_region (self, clip_region); } static void meta_background_actor_reset_culling (MetaCullable *cullable) { MetaBackgroundActor *self = META_BACKGROUND_ACTOR (cullable); 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) { MetaBackgroundActorPrivate *priv = self->priv; return priv->clip_region; } static void invalidate_pipeline (MetaBackgroundActor *self, ChangedFlags changed) { MetaBackgroundActorPrivate *priv = self->priv; priv->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) { MetaBackgroundActorPrivate *priv; g_return_if_fail (META_IS_BACKGROUND_ACTOR (self)); g_return_if_fail (background == NULL || META_IS_BACKGROUND (background)); priv = self->priv; if (background == priv->background) return; if (priv->background) { g_signal_handlers_disconnect_by_func (priv->background, (gpointer)on_background_changed, self); g_object_unref (priv->background); priv->background = NULL; } if (background) { priv->background = g_object_ref (background); g_signal_connect (priv->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) { MetaBackgroundActorPrivate *priv; 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.); priv = self->priv; enabled = enabled != FALSE && height != 0; if (enabled != priv->gradient) { priv->gradient = enabled; invalidate_pipeline (self, CHANGED_EFFECTS); changed = TRUE; } if (height != priv->gradient_height || max_darkness != priv->gradient_max_darkness) { priv->gradient_height = height; priv->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) { MetaBackgroundActorPrivate *priv = self->priv; MetaRectangle old_monitor_geometry; MetaRectangle new_monitor_geometry; if(priv->monitor == monitor) return; meta_screen_get_monitor_geometry (priv->screen, priv->monitor, &old_monitor_geometry); meta_screen_get_monitor_geometry (priv->screen, monitor, &new_monitor_geometry); if(old_monitor_geometry.height != new_monitor_geometry.height) invalidate_pipeline (self, CHANGED_GRADIENT_PARAMETERS); priv->monitor = monitor; } void meta_background_actor_set_vignette (MetaBackgroundActor *self, gboolean enabled, double brightness, double sharpness) { MetaBackgroundActorPrivate *priv; 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.); priv = self->priv; enabled = enabled != FALSE; if (enabled != priv->vignette) { priv->vignette = enabled; invalidate_pipeline (self, CHANGED_EFFECTS); changed = TRUE; } if (brightness != priv->vignette_brightness || sharpness != priv->vignette_sharpness) { priv->vignette_brightness = brightness; priv->vignette_sharpness = sharpness; invalidate_pipeline (self, CHANGED_VIGNETTE_PARAMETERS); changed = TRUE; } if (changed) clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } ukwm/src/compositor/meta-texture-rectangle.c0000664000175000017500000000406513220600404020176 0ustar fengfeng/* * texture rectangle * * A small utility function to help create a rectangle texture * * Authored By Neil Roberts * * Copyright (C) 2011, 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, see . */ #include #include #include "meta-texture-rectangle.h" static void texture_rectangle_check_cb (CoglTexture *sub_texture, const float *sub_texture_coords, const float *meta_coords, void *user_data) { gboolean *result = user_data; if (cogl_is_texture_rectangle (sub_texture)) *result = TRUE; } /* Determines if the given texture is using a rectangle texture as its * primitive texture type. Eventually this function could be replaced * with cogl_texture_get_type if Cogl makes that public. * * http://git.gnome.org/browse/cogl/commit/?h=8012eee31 */ gboolean meta_texture_rectangle_check (CoglTexture *texture) { gboolean result = FALSE; cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (texture), 0.0f, 0.0f, /* tx_1 / ty_1 */ 1.0f, 1.0f, /* tx_2 / ty_2 */ COGL_PIPELINE_WRAP_MODE_REPEAT, COGL_PIPELINE_WRAP_MODE_REPEAT, texture_rectangle_check_cb, &result); return result; } ukwm/src/compositor/meta-surface-actor.h0000664000175000017500000000652513220600404017302 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef META_SURFACE_ACTOR_PRIVATE_H #define META_SURFACE_ACTOR_PRIVATE_H #include #include #include G_BEGIN_DECLS #define META_TYPE_SURFACE_ACTOR (meta_surface_actor_get_type()) #define META_SURFACE_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_SURFACE_ACTOR, MetaSurfaceActor)) #define META_SURFACE_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_SURFACE_ACTOR, MetaSurfaceActorClass)) #define META_IS_SURFACE_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_SURFACE_ACTOR)) #define META_IS_SURFACE_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_SURFACE_ACTOR)) #define META_SURFACE_ACTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_SURFACE_ACTOR, MetaSurfaceActorClass)) typedef struct _MetaSurfaceActor MetaSurfaceActor; typedef struct _MetaSurfaceActorClass MetaSurfaceActorClass; typedef struct _MetaSurfaceActorPrivate MetaSurfaceActorPrivate; 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_visible) (MetaSurfaceActor *actor); gboolean (* should_unredirect) (MetaSurfaceActor *actor); void (* set_unredirected) (MetaSurfaceActor *actor, gboolean unredirected); gboolean (* is_unredirected) (MetaSurfaceActor *actor); MetaWindow *(* get_window) (MetaSurfaceActor *actor); }; struct _MetaSurfaceActor { ClutterActor parent; MetaSurfaceActorPrivate *priv; }; GType meta_surface_actor_get_type (void); cairo_surface_t *meta_surface_actor_get_image (MetaSurfaceActor *self, cairo_rectangle_int_t *clip); MetaShapedTexture *meta_surface_actor_get_texture (MetaSurfaceActor *self); MetaWindow *meta_surface_actor_get_window (MetaSurfaceActor *self); 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_argb32 (MetaSurfaceActor *actor); gboolean meta_surface_actor_is_visible (MetaSurfaceActor *actor); void meta_surface_actor_set_frozen (MetaSurfaceActor *actor, gboolean frozen); gboolean meta_surface_actor_should_unredirect (MetaSurfaceActor *actor); void meta_surface_actor_set_unredirected (MetaSurfaceActor *actor, gboolean unredirected); gboolean meta_surface_actor_is_unredirected (MetaSurfaceActor *actor); G_END_DECLS #endif /* META_SURFACE_ACTOR_PRIVATE_H */ ukwm/src/compositor/compositor.c0000664000175000017500000013526513220600404016015 0ustar fengfeng/* -*- 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_screen() * - top window group, accessible with meta_get_top_window_group_for_screen() * * Ukwm 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 #include #include "core.h" #include #include #include #include "compositor-private.h" #include #include #include #include #include #include #include #include "meta-window-actor-private.h" #include "meta-window-group.h" #include "window-private.h" /* to check window->hidden */ #include "display-private.h" /* for meta_display_lookup_x_window() and meta_display_cancel_touch() */ #include "util-private.h" #include "backends/meta-dnd-private.h" #include "frame.h" #include #include #include "meta-sync-ring.h" #include "backends/x11/meta-backend-x11.h" #include "clutter/clutter-ukwm.h" #ifdef HAVE_WAYLAND #include "wayland/meta-wayland-private.h" #endif static void on_presented (ClutterStage *stage, CoglFrameEvent event, ClutterFrameInfo *frame_info, 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) { GList *l; /* Finish hiding and showing actors for the new workspace */ for (l = compositor->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) { /* FIXME -- must redo stacking order */ compositor->switch_workspace_in_progress--; if (compositor->switch_workspace_in_progress < 0) { g_warning ("Error in workspace_switch accounting!"); compositor->switch_workspace_in_progress = 0; } if (!compositor->switch_workspace_in_progress) meta_finish_workspace_switch (compositor); } void meta_compositor_destroy (MetaCompositor *compositor) { clutter_threads_remove_repaint_func (compositor->pre_paint_func_id); clutter_threads_remove_repaint_func (compositor->post_paint_func_id); if (compositor->have_x11_sync_object) meta_sync_ring_destroy (); } static void process_damage (MetaCompositor *compositor, XDamageNotifyEvent *event, MetaWindow *window) { MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window)); meta_window_actor_process_x11_damage (window_actor, event); compositor->frame_has_updated_xsurfaces = TRUE; } /* compat helper */ static MetaCompositor * get_compositor_for_screen (MetaScreen *screen) { return screen->display->compositor; } /** * meta_get_stage_for_screen: * @screen: a #MetaScreen * * Returns: (transfer none): The #ClutterStage for the screen */ ClutterActor * meta_get_stage_for_screen (MetaScreen *screen) { MetaCompositor *compositor = get_compositor_for_screen (screen); return compositor->stage; } /** * meta_get_window_group_for_screen: * @screen: a #MetaScreen * * Returns: (transfer none): The window group corresponding to @screen */ ClutterActor * meta_get_window_group_for_screen (MetaScreen *screen) { MetaCompositor *compositor = get_compositor_for_screen (screen); return compositor->window_group; } /** * meta_get_top_window_group_for_screen: * @screen: a #MetaScreen * * Returns: (transfer none): The top window group corresponding to @screen */ ClutterActor * meta_get_top_window_group_for_screen (MetaScreen *screen) { MetaCompositor *compositor = get_compositor_for_screen (screen); return compositor->top_window_group; } /** * meta_get_feedback_group_for_screen: * @screen: a #MetaScreen * * Returns: (transfer none): The feedback group corresponding to @screen */ ClutterActor * meta_get_feedback_group_for_screen (MetaScreen *screen) { MetaCompositor *compositor = get_compositor_for_screen (screen); return compositor->feedback_group; } /** * meta_get_window_actors: * @screen: a #MetaScreen * * Returns: (transfer none) (element-type Clutter.Actor): The set of #MetaWindowActor on @screen */ GList * meta_get_window_actors (MetaScreen *screen) { MetaCompositor *compositor = get_compositor_for_screen (screen); return compositor->windows; } void meta_set_stage_input_region (MetaScreen *screen, XserverRegion region) { /* As a wayland compositor we can simply ignore all this trickery * for setting an input region on the stage for capturing events in * clutter since all input comes to us first and we get to choose * who else sees them. */ if (!meta_is_wayland_compositor ()) { MetaDisplay *display = screen->display; MetaCompositor *compositor = display->compositor; Display *xdpy = meta_display_get_xdisplay (display); Window xstage = clutter_x11_get_stage_window (CLUTTER_STAGE (compositor->stage)); XFixesSetWindowShapeRegion (xdpy, xstage, 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 (display, XNextRequest (xdpy)); XFixesSetWindowShapeRegion (xdpy, compositor->output, ShapeInput, 0, 0, region); } } void meta_empty_stage_input_region (MetaScreen *screen) { /* Using a static region here is a bit hacky, but Metacity never opens more than * one XDisplay, so it works fine. */ static XserverRegion region = None; if (region == None) { MetaDisplay *display = meta_screen_get_display (screen); Display *xdpy = meta_display_get_xdisplay (display); region = XFixesCreateRegion (xdpy, NULL, 0); } meta_set_stage_input_region (screen, region); } void meta_focus_stage_window (MetaScreen *screen, guint32 timestamp) { ClutterStage *stage; Window window; stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen)); if (!stage) return; window = clutter_x11_get_stage_window (stage); if (window == None) return; meta_display_set_input_focus_xwindow (screen->display, screen, window, timestamp); } gboolean meta_stage_is_focused (MetaScreen *screen) { ClutterStage *stage; Window window; if (meta_is_wayland_compositor ()) return TRUE; stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen)); if (!stage) return FALSE; window = clutter_x11_get_stage_window (stage); if (window == None) return FALSE; return (screen->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. */ MetaDisplay *display = compositor->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; /* XXX: why is this needed? */ XIUngrabDevice (display->xdisplay, META_VIRTUAL_CORE_POINTER_ID, timestamp); XSync (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_screen (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) { MetaDisplay *display = compositor->display; MetaBackend *backend = meta_get_backend (); g_return_if_fail (is_modal (display)); g_signal_emit_by_name (display, "grab-op-end", meta_plugin_get_screen (plugin), display->grab_window, display->grab_op); 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); if (meta_is_wayland_compositor ()) meta_display_sync_wayland_input_focus (display); } static void after_stage_paint (ClutterStage *stage, gpointer data) { MetaCompositor *compositor = data; GList *l; for (l = compositor->windows; l; l = l->next) meta_window_actor_post_paint (l->data); #ifdef HAVE_WAYLAND if (meta_is_wayland_compositor ()) meta_wayland_compositor_paint_finished (meta_wayland_compositor_get_default ()); #endif } static void redirect_windows (MetaScreen *screen) { MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); Window xroot = meta_screen_get_xroot (screen); int screen_number = meta_screen_get_screen_number (screen); 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 Ukwm) 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_error_trap_push (display); XCompositeRedirectSubwindows (xdisplay, xroot, CompositeRedirectManual); XSync (xdisplay, FALSE); if (!meta_error_trap_pop_with_return (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, display->name); } n_retries++; g_usleep (G_USEC_PER_SEC); } } void meta_compositor_manage (MetaCompositor *compositor) { MetaDisplay *display = compositor->display; Display *xdisplay = display->xdisplay; MetaScreen *screen = display->screen; MetaBackend *backend = meta_get_backend (); meta_screen_set_cm_selection (display->screen); compositor->stage = meta_backend_get_stage (backend); g_signal_connect (compositor->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. */ g_signal_connect_after (CLUTTER_STAGE (compositor->stage), "after-paint", G_CALLBACK (after_stage_paint), compositor); clutter_stage_set_sync_delay (CLUTTER_STAGE (compositor->stage), META_SYNC_DELAY); compositor->window_group = meta_window_group_new (screen); compositor->top_window_group = meta_window_group_new (screen); compositor->feedback_group = meta_window_group_new (screen); clutter_actor_add_child (compositor->stage, compositor->window_group); clutter_actor_add_child (compositor->stage, compositor->top_window_group); clutter_actor_add_child (compositor->stage, compositor->feedback_group); if (meta_is_wayland_compositor ()) { /* NB: When running as a wayland compositor we don't need an X * composite overlay window, and we don't need to play any input * region tricks to redirect events into clutter. */ compositor->output = None; } else { Window xwin; compositor->output = screen->composite_overlay_window; xwin = meta_backend_x11_get_xwindow (META_BACKEND_X11 (backend)); XReparentWindow (xdisplay, xwin, compositor->output, 0, 0); meta_empty_stage_input_region (screen); /* 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->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->output); compositor->have_x11_sync_object = meta_sync_ring_init (xdisplay); } redirect_windows (display->screen); compositor->plugin_mgr = meta_plugin_manager_new (compositor); } void meta_compositor_unmanage (MetaCompositor *compositor) { if (!meta_is_wayland_compositor ()) { MetaDisplay *display = compositor->display; Display *xdisplay = meta_display_get_xdisplay (display); Window xroot = display->screen->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); } } /** * meta_shape_cow_for_window: * @compositor: A #MetaCompositor * @window: (nullable): A #MetaWindow to shape the COW for * * 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 meta_shape_cow_for_window (MetaCompositor *compositor, MetaWindow *window) { MetaDisplay *display = compositor->display; Display *xdisplay = meta_display_get_xdisplay (display); if (window == NULL) XFixesSetWindowShapeRegion (xdisplay, compositor->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_screen_get_size (display->screen, &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->output, ShapeBounding, 0, 0, output_region); XFixesDestroyRegion (xdisplay, output_region); } } static void set_unredirected_window (MetaCompositor *compositor, MetaWindow *window) { if (compositor->unredirected_window == window) return; if (compositor->unredirected_window != NULL) { MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (compositor->unredirected_window)); meta_window_actor_set_unredirected (window_actor, FALSE); } meta_shape_cow_for_window (compositor, window); compositor->unredirected_window = window; if (compositor->unredirected_window != NULL) { MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (compositor->unredirected_window)); meta_window_actor_set_unredirected (window_actor, TRUE); } } void meta_compositor_add_window (MetaCompositor *compositor, MetaWindow *window) { MetaDisplay *display = compositor->display; meta_error_trap_push (display); meta_window_actor_new (window); sync_actor_stacking (compositor); meta_error_trap_pop (display); } void meta_compositor_remove_window (MetaCompositor *compositor, MetaWindow *window) { MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window)); if (compositor->unredirected_window == window) set_unredirected_window (compositor, NULL); if (compositor->top_window_actor == window_actor) compositor->top_window_actor = NULL; meta_window_actor_destroy (window_actor); } void meta_compositor_sync_updates_frozen (MetaCompositor *compositor, MetaWindow *window) { MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (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 (meta_window_get_compositor_private (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 (meta_window_get_compositor_private (window)); if (!window_actor) return; meta_window_actor_update_shape (window_actor); } void meta_compositor_window_opacity_changed (MetaCompositor *compositor, MetaWindow *window) { MetaWindowActor *window_actor; window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window)); if (!window_actor) return; meta_window_actor_update_opacity (window_actor); } void meta_compositor_window_surface_changed (MetaCompositor *compositor, MetaWindow *window) { MetaWindowActor *window_actor; window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window)); if (!window_actor) return; meta_window_actor_update_surface (window_actor); } /** * meta_compositor_process_event: (skip) * @compositor: * @event: * @window: * */ gboolean meta_compositor_process_event (MetaCompositor *compositor, XEvent *event, MetaWindow *window) { if (!meta_is_wayland_compositor () && event->type == meta_display_get_damage_event_base (compositor->display) + XDamageNotify) { /* Core code doesn't handle damage events, so we need to extract the MetaWindow * ourselves */ if (window == NULL) { Window xwin = ((XDamageNotifyEvent *) event)->drawable; window = meta_display_lookup_x_window (compositor->display, xwin); } if (window) process_damage (compositor, (XDamageNotifyEvent *) event, window); } if (compositor->have_x11_sync_object) meta_sync_ring_handle_event (event); /* Clutter needs to know about MapNotify events otherwise it will think the stage is invisible */ if (!meta_is_wayland_compositor () && event->type == MapNotify) clutter_x11_handle_event (event); /* The above handling is basically just "observing" the events, so we return * FALSE to indicate that the event should not be filtered out; if we have * GTK+ windows in the same process, GTK+ needs the ConfigureNotify event, for example. */ return FALSE; } gboolean meta_compositor_filter_keybinding (MetaCompositor *compositor, MetaKeyBinding *binding) { return meta_plugin_manager_filter_keybinding (compositor->plugin_mgr, binding); } void meta_compositor_show_window (MetaCompositor *compositor, MetaWindow *window, MetaCompEffect effect) { MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window)); meta_window_actor_show (window_actor, effect); } void meta_compositor_hide_window (MetaCompositor *compositor, MetaWindow *window, MetaCompEffect effect) { MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window)); meta_window_actor_hide (window_actor, effect); } 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 (meta_window_get_compositor_private (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) { gint to_indx, from_indx; to_indx = meta_workspace_index (to); from_indx = meta_workspace_index (from); compositor->switch_workspace_in_progress++; if (!meta_plugin_manager_switch_workspace (compositor->plugin_mgr, from_indx, to_indx, direction)) { compositor->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) { 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 (compositor->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 = compositor->windows; for (old = children; old != NULL; old = old->next) { ClutterActor *actor = old->data; if (META_IS_BACKGROUND_GROUP (actor) || META_IS_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_list_free (children); 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 (compositor->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); } /* 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) { GList *l; for (l = g_list_last (compositor->windows); l; l = l->prev) { MetaWindowActor *window_actor = l->data; MetaWindow *window = meta_window_actor_get_meta_window (window_actor); MetaRectangle buffer_rect; meta_window_get_buffer_rect (window, &buffer_rect); if (meta_rectangle_overlap (&compositor->display->screen->rect, &buffer_rect)) return window_actor; } return NULL; } void meta_compositor_sync_stack (MetaCompositor *compositor, GList *stack) { 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 (compositor->windows); /* The old stack of MetaWindowActor */ compositor->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 (meta_window_get_compositor_private (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.) */ compositor->windows = g_list_prepend (compositor->windows, actor); stack = g_list_remove (stack, window); old_stack = g_list_remove (old_stack, actor); } sync_actor_stacking (compositor); compositor->top_window_actor = get_top_visible_window_actor (compositor); } void meta_compositor_sync_window_geometry (MetaCompositor *compositor, MetaWindow *window, gboolean did_placement) { MetaWindowActor *window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window)); meta_window_actor_sync_actor_geometry (window_actor, did_placement); meta_plugin_manager_event_size_changed (compositor->plugin_mgr, window_actor); } static void on_presented (ClutterStage *stage, CoglFrameEvent event, ClutterFrameInfo *frame_info, MetaCompositor *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 (compositor->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 = compositor->windows; l; l = l->next) meta_window_actor_frame_complete (l->data, frame_info, presentation_time); } } static gboolean meta_pre_paint_func (gpointer data) { GList *l; MetaWindowActor *top_window_actor; MetaCompositor *compositor = data; if (compositor->windows == NULL) return TRUE; top_window_actor = compositor->top_window_actor; if (top_window_actor && meta_window_actor_should_unredirect (top_window_actor) && compositor->disable_unredirect_count == 0) { MetaWindow *top_window; top_window = meta_window_actor_get_meta_window (top_window_actor); set_unredirected_window (compositor, top_window); } else { set_unredirected_window (compositor, NULL); } for (l = compositor->windows; l; l = l->next) meta_window_actor_pre_paint (l->data); if (compositor->frame_has_updated_xsurfaces) { /* 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->have_x11_sync_object) compositor->have_x11_sync_object = meta_sync_ring_insert_wait (); else XSync (compositor->display->xdisplay, False); } return TRUE; } static gboolean meta_post_paint_func (gpointer data) { MetaCompositor *compositor = data; CoglGraphicsResetStatus status; if (compositor->frame_has_updated_xsurfaces) { if (compositor->have_x11_sync_object) compositor->have_x11_sync_object = meta_sync_ring_after_frame (); compositor->frame_has_updated_xsurfaces = FALSE; } status = cogl_get_graphics_reset_status (compositor->context); switch (status) { case COGL_GRAPHICS_RESET_STATUS_NO_ERROR: break; case COGL_GRAPHICS_RESET_STATUS_PURGED_CONTEXT_RESET: g_signal_emit_by_name (compositor->display, "gl-video-memory-purged"); clutter_actor_queue_redraw (CLUTTER_ACTOR (compositor->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; } return TRUE; } static void on_shadow_factory_changed (MetaShadowFactory *factory, MetaCompositor *compositor) { GList *l; for (l = compositor->windows; l; l = l->next) meta_window_actor_invalidate_shadow (l->data); } /** * meta_compositor_new: (skip) * @display: * */ MetaCompositor * meta_compositor_new (MetaDisplay *display) { MetaBackend *backend = meta_get_backend (); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); MetaCompositor *compositor; compositor = g_new0 (MetaCompositor, 1); compositor->display = display; compositor->context = clutter_backend->cogl_context; if (g_getenv("META_DISABLE_MIPMAPS")) compositor->no_mipmaps = TRUE; g_signal_connect (meta_shadow_factory_get_default (), "changed", G_CALLBACK (on_shadow_factory_changed), compositor); compositor->pre_paint_func_id = clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_PRE_PAINT, meta_pre_paint_func, compositor, NULL); compositor->post_paint_func_id = clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT, meta_post_paint_func, compositor, NULL); return compositor; } /** * meta_get_overlay_window: (skip) * @screen: a #MetaScreen * */ Window meta_get_overlay_window (MetaScreen *screen) { MetaCompositor *compositor = get_compositor_for_screen (screen); return compositor->output; } /** * meta_disable_unredirect_for_screen: * @screen: a #MetaScreen * * Disables unredirection, can be usefull in situations where having * unredirected windows is undesireable like when recording a video. * */ void meta_disable_unredirect_for_screen (MetaScreen *screen) { MetaCompositor *compositor = get_compositor_for_screen (screen); compositor->disable_unredirect_count++; } /** * meta_enable_unredirect_for_screen: * @screen: a #MetaScreen * * Enables unredirection which reduces the overhead for apps like games. * */ void meta_enable_unredirect_for_screen (MetaScreen *screen) { MetaCompositor *compositor = get_compositor_for_screen (screen); if (compositor->disable_unredirect_count == 0) g_warning ("Called enable_unredirect_for_screen while unredirection is enabled."); if (compositor->disable_unredirect_count > 0) compositor->disable_unredirect_count--; } #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_screen (MetaCompositor *compositor, MetaScreen *screen) { ClutterActor *stage; ClutterActor *flash; ClutterTransition *transition; gfloat width, height; stage = meta_get_stage_for_screen (screen); 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_get_compositor_private (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_time_to_server_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. */ gint64 meta_compositor_monotonic_time_to_server_time (MetaDisplay *display, gint64 monotonic_time) { MetaCompositor *compositor = display->compositor; if (compositor->server_time_query_time == 0 || (!compositor->server_time_is_monotonic_time && monotonic_time > compositor->server_time_query_time + 10*1000*1000)) /* 10 seconds */ { guint32 server_time = meta_display_get_current_time_roundtrip (display); gint64 server_time_usec = (gint64)server_time * 1000; gint64 current_monotonic_time = g_get_monotonic_time (); compositor->server_time_query_time = current_monotonic_time; /* If the server time 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 (server_time_usec > current_monotonic_time - 1000*1000 && server_time_usec < current_monotonic_time + 1000*1000) compositor->server_time_is_monotonic_time = TRUE; compositor->server_time_offset = server_time_usec - current_monotonic_time; } if (compositor->server_time_is_monotonic_time) return monotonic_time; else return monotonic_time + compositor->server_time_offset; } void meta_compositor_show_tile_preview (MetaCompositor *compositor, MetaWindow *window, MetaRectangle *tile_rect, int tile_monitor_number) { meta_plugin_manager_show_tile_preview (compositor->plugin_mgr, window, tile_rect, tile_monitor_number); } void meta_compositor_hide_tile_preview (MetaCompositor *compositor) { meta_plugin_manager_hide_tile_preview (compositor->plugin_mgr); } void meta_compositor_show_window_menu (MetaCompositor *compositor, MetaWindow *window, MetaWindowMenuType menu, int x, int y) { meta_plugin_manager_show_window_menu (compositor->plugin_mgr, window, menu, x, y); } void meta_compositor_show_window_menu_for_rect (MetaCompositor *compositor, MetaWindow *window, MetaWindowMenuType menu, MetaRectangle *rect) { meta_plugin_manager_show_window_menu_for_rect (compositor->plugin_mgr, window, menu, rect); } MetaCloseDialog * meta_compositor_create_close_dialog (MetaCompositor *compositor, MetaWindow *window) { return meta_plugin_manager_create_close_dialog (compositor->plugin_mgr, window); } MetaInhibitShortcutsDialog * meta_compositor_create_inhibit_shortcuts_dialog (MetaCompositor *compositor, MetaWindow *window) { return meta_plugin_manager_create_inhibit_shortcuts_dialog (compositor->plugin_mgr, window); } ukwm/src/compositor/meta-sync-ring.h0000664000175000017500000000053613220600404016451 0ustar fengfeng#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_ */ ukwm/src/compositor/cogl-utils.h0000664000175000017500000000247713220600404015704 0ustar fengfeng/* -*- 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 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__ */ ukwm/src/compositor/meta-dnd.c0000664000175000017500000002016213220600404015275 0ustar fengfeng/* -*- 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 "meta/meta-dnd.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) { } 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, MetaCompositor *compositor, MetaDisplay *display, XEvent *xev) { MetaDnd *dnd = meta_backend_get_dnd (backend); Window output_window = compositor->output; if (xev->xany.type != ClientMessage) return FALSE; if (xev->xany.window != output_window && xev->xany.window != clutter_x11_get_stage_window (CLUTTER_STAGE (compositor->stage))) return FALSE; if (xev->xclient.message_type == gdk_x11_get_xatom_by_name ("XdndPosition")) { XEvent xevent; Window src = xev->xclient.data.l[0]; memset (&xevent, 0, sizeof(xevent)); xevent.xany.type = ClientMessage; xevent.xany.display = display->xdisplay; xevent.xclient.window = src; xevent.xclient.message_type = gdk_x11_get_xatom_by_name ("XdndStatus"); 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 (display->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 == gdk_x11_get_xatom_by_name ("XdndLeave")) { meta_dnd_notify_dnd_leave (dnd); return TRUE; } else if (xev->xclient.message_type == gdk_x11_get_xatom_by_name ("XdndEnter")) { 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); unsigned int i; meta_wayland_data_device_end_drag (&priv->wl_compositor->seat->data_device); for (i = 0; i < G_N_ELEMENTS (priv->handler_id); i++) { g_signal_handler_disconnect (priv->compositor->stage, priv->handler_id[i]); priv->handler_id[i] = 0; } priv->compositor = NULL; priv->wl_compositor = NULL; meta_dnd_notify_dnd_leave (dnd); } 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) { priv->compositor = compositor; priv->wl_compositor = wl_compositor; priv->handler_id[0] = g_signal_connect (compositor->stage, "motion-event", G_CALLBACK (meta_dnd_wayland_on_motion_event), dnd); priv->handler_id[1] = g_signal_connect (compositor->stage, "button-release-event", G_CALLBACK (meta_dnd_wayland_on_button_released), dnd); priv->handler_id[2] = g_signal_connect (compositor->stage, "key-press-event", G_CALLBACK (meta_dnd_wayland_on_key_pressed), dnd); meta_dnd_notify_dnd_enter (dnd); } } #endif ukwm/src/compositor/meta-texture-tower.c0000664000175000017500000003617113220600404017375 0ustar fengfeng/* -*- 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 #include #include "meta-texture-tower.h" #include "meta-texture-rectangle.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 (int width, int height) { 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) */ cogl_get_projection_matrix (&projection); cogl_get_modelview_matrix (&modelview); cogl_matrix_multiply (&pm, &projection, &modelview); cogl_get_viewport (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 gboolean is_power_of_two (int x) { return (x & (x - 1)) == 0; } static void texture_tower_create_texture (MetaTextureTower *tower, int level, int width, int height) { if ((!is_power_of_two (width) || !is_power_of_two (height)) && meta_texture_rectangle_check (tower->textures[level - 1])) { ClutterBackend *backend = clutter_get_default_backend (); CoglContext *context = clutter_backend_get_cogl_context (backend); CoglTextureRectangle *texture_rectangle; texture_rectangle = cogl_texture_rectangle_new_with_size (context, width, height); tower->textures[level] = COGL_TEXTURE (texture_rectangle); } else { 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; CoglError *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)) { cogl_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 * * 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) { 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(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]; } ukwm/src/compositor/meta-shaped-texture.c0000664000175000017500000007466513220600404017513 0ustar fengfeng/* * 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 #include #include "meta-shaped-texture-private.h" #include #include /* for gdk_rectangle_intersect() */ #include "clutter-utils.h" #include "meta-texture-tower.h" #include "meta-cullable.h" static void meta_shaped_texture_dispose (GObject *object); static void meta_shaped_texture_paint (ClutterActor *actor); static void meta_shaped_texture_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p); static void meta_shaped_texture_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p); static gboolean meta_shaped_texture_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume); static void cullable_iface_init (MetaCullableInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaShapedTexture, meta_shaped_texture, CLUTTER_TYPE_ACTOR, G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init)); #define META_SHAPED_TEXTURE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), META_TYPE_SHAPED_TEXTURE, \ MetaShapedTexturePrivate)) enum { SIZE_CHANGED, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL]; struct _MetaShapedTexturePrivate { 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; cairo_region_t *unobscured_region; guint tex_width, tex_height; guint fallback_width, fallback_height; guint create_mipmaps : 1; }; static void meta_shaped_texture_class_init (MetaShapedTextureClass *klass) { GObjectClass *gobject_class = (GObjectClass *) klass; ClutterActorClass *actor_class = (ClutterActorClass *) klass; gobject_class->dispose = meta_shaped_texture_dispose; actor_class->get_preferred_width = meta_shaped_texture_get_preferred_width; actor_class->get_preferred_height = meta_shaped_texture_get_preferred_height; actor_class->paint = meta_shaped_texture_paint; actor_class->get_paint_volume = meta_shaped_texture_get_paint_volume; 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); g_type_class_add_private (klass, sizeof (MetaShapedTexturePrivate)); } static void meta_shaped_texture_init (MetaShapedTexture *self) { MetaShapedTexturePrivate *priv; priv = self->priv = META_SHAPED_TEXTURE_GET_PRIVATE (self); priv->paint_tower = meta_texture_tower_new (); priv->texture = NULL; priv->mask_texture = NULL; priv->create_mipmaps = TRUE; priv->is_y_inverted = TRUE; } static void set_unobscured_region (MetaShapedTexture *self, cairo_region_t *unobscured_region) { MetaShapedTexturePrivate *priv = self->priv; g_clear_pointer (&priv->unobscured_region, (GDestroyNotify) cairo_region_destroy); if (unobscured_region) { guint width, height; if (priv->texture) { width = priv->tex_width; height = priv->tex_height; } else { width = priv->fallback_width; height = priv->fallback_height; } cairo_rectangle_int_t bounds = { 0, 0, width, height }; priv->unobscured_region = cairo_region_copy (unobscured_region); cairo_region_intersect_rectangle (priv->unobscured_region, &bounds); } } static void set_clip_region (MetaShapedTexture *self, cairo_region_t *clip_region) { MetaShapedTexturePrivate *priv = self->priv; g_clear_pointer (&priv->clip_region, (GDestroyNotify) cairo_region_destroy); if (clip_region) priv->clip_region = cairo_region_copy (clip_region); } static void meta_shaped_texture_reset_pipelines (MetaShapedTexture *stex) { MetaShapedTexturePrivate *priv = stex->priv; g_clear_pointer (&priv->base_pipeline, cogl_object_unref); g_clear_pointer (&priv->masked_pipeline, cogl_object_unref); g_clear_pointer (&priv->unblended_pipeline, cogl_object_unref); } static void meta_shaped_texture_dispose (GObject *object) { MetaShapedTexture *self = (MetaShapedTexture *) object; MetaShapedTexturePrivate *priv = self->priv; if (priv->paint_tower) meta_texture_tower_free (priv->paint_tower); priv->paint_tower = NULL; g_clear_pointer (&priv->texture, cogl_object_unref); g_clear_pointer (&priv->opaque_region, cairo_region_destroy); meta_shaped_texture_set_mask_texture (self, NULL); set_unobscured_region (self, NULL); set_clip_region (self, NULL); meta_shaped_texture_reset_pipelines (self); g_clear_pointer (&priv->snippet, cogl_object_unref); G_OBJECT_CLASS (meta_shaped_texture_parent_class)->dispose (object); } static CoglPipeline * get_base_pipeline (MetaShapedTexture *stex, CoglContext *ctx) { MetaShapedTexturePrivate *priv = stex->priv; CoglPipeline *pipeline; if (priv->base_pipeline) return priv->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); if (!priv->is_y_inverted) { CoglMatrix matrix; cogl_matrix_init_identity (&matrix); cogl_matrix_scale (&matrix, 1, -1, 1); cogl_matrix_translate (&matrix, 0, -1, 0); cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix); } if (priv->snippet) cogl_pipeline_add_layer_snippet (pipeline, 0, priv->snippet); priv->base_pipeline = pipeline; return priv->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) { MetaShapedTexturePrivate *priv = stex->priv; CoglPipeline *pipeline; if (priv->masked_pipeline) return priv->masked_pipeline; pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx)); cogl_pipeline_set_layer_combine (pipeline, 1, "RGBA = MODULATE (PREVIOUS, TEXTURE[A])", NULL); priv->masked_pipeline = pipeline; return pipeline; } static CoglPipeline * get_unblended_pipeline (MetaShapedTexture *stex, CoglContext *ctx) { MetaShapedTexturePrivate *priv = stex->priv; CoglPipeline *pipeline; CoglColor color; if (priv->unblended_pipeline) return priv->unblended_pipeline; pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx)); cogl_color_init_from_4ub (&color, 255, 255, 255, 255); cogl_pipeline_set_blend (pipeline, "RGBA = ADD (SRC_COLOR, 0)", NULL); cogl_pipeline_set_color (pipeline, &color); priv->unblended_pipeline = pipeline; return pipeline; } static void paint_clipped_rectangle (CoglFramebuffer *fb, CoglPipeline *pipeline, cairo_rectangle_int_t *rect, ClutterActorBox *alloc) { float coords[8]; float x1, y1, x2, y2; x1 = rect->x; y1 = rect->y; x2 = rect->x + rect->width; y2 = rect->y + rect->height; coords[0] = rect->x / (alloc->x2 - alloc->x1); coords[1] = rect->y / (alloc->y2 - alloc->y1); coords[2] = (rect->x + rect->width) / (alloc->x2 - alloc->x1); coords[3] = (rect->y + rect->height) / (alloc->y2 - alloc->y1); coords[4] = coords[0]; coords[5] = coords[1]; coords[6] = coords[2]; coords[7] = coords[3]; cogl_framebuffer_draw_multitextured_rectangle (fb, pipeline, x1, y1, x2, y2, &coords[0], 8); } static void set_cogl_texture (MetaShapedTexture *stex, CoglTexture *cogl_tex) { MetaShapedTexturePrivate *priv; guint width, height; g_return_if_fail (META_IS_SHAPED_TEXTURE (stex)); priv = stex->priv; if (priv->texture) cogl_object_unref (priv->texture); priv->texture = cogl_tex; if (cogl_tex != NULL) { 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 (priv->tex_width != width || priv->tex_height != height) { priv->tex_width = width; priv->tex_height = height; meta_shaped_texture_set_mask_texture (stex, NULL); clutter_actor_queue_relayout (CLUTTER_ACTOR (stex)); g_signal_emit (stex, signals[SIZE_CHANGED], 0); } /* 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 (priv->create_mipmaps) meta_texture_tower_set_base_texture (priv->paint_tower, cogl_tex); } static void meta_shaped_texture_paint (ClutterActor *actor) { MetaShapedTexture *stex = (MetaShapedTexture *) actor; MetaShapedTexturePrivate *priv = stex->priv; guint tex_width, tex_height; guchar opacity; CoglContext *ctx; CoglFramebuffer *fb; CoglTexture *paint_tex; ClutterActorBox alloc; CoglPipelineFilter filter; if (priv->clip_region && cairo_region_is_empty (priv->clip_region)) return; if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (stex))) clutter_actor_realize (CLUTTER_ACTOR (stex)); /* 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. */ if (priv->create_mipmaps) paint_tex = meta_texture_tower_get_paint_texture (priv->paint_tower); else paint_tex = COGL_TEXTURE (priv->texture); if (paint_tex == NULL) return; tex_width = priv->tex_width; tex_height = priv->tex_height; if (tex_width == 0 || tex_height == 0) /* no contents yet */ return; cairo_rectangle_int_t tex_rect = { 0, 0, tex_width, tex_height }; /* Use nearest-pixel interpolation if the texture is unscaled. This * improves performance, especially with software rendering. */ filter = COGL_PIPELINE_FILTER_LINEAR; if (meta_actor_painting_untransformed (tex_width, tex_height, NULL, NULL)) filter = COGL_PIPELINE_FILTER_NEAREST; ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); fb = cogl_get_draw_framebuffer (); opacity = clutter_actor_get_paint_opacity (actor); clutter_actor_get_allocation_box (actor, &alloc); cairo_region_t *blended_region; gboolean use_opaque_region = (priv->opaque_region != NULL && opacity == 255); if (use_opaque_region) { if (priv->clip_region != NULL) blended_region = cairo_region_copy (priv->clip_region); else blended_region = cairo_region_create_rectangle (&tex_rect); cairo_region_subtract (blended_region, priv->opaque_region); } else { if (priv->clip_region != NULL) blended_region = cairo_region_reference (priv->clip_region); else blended_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_region != NULL) { int n_rects = cairo_region_num_rectangles (blended_region); if (n_rects > MAX_RECTS) { /* Fall back to taking the fully blended path. */ use_opaque_region = FALSE; cairo_region_destroy (blended_region); blended_region = NULL; } } /* First, paint the unblended parts, which are part of the opaque region. */ if (use_opaque_region) { CoglPipeline *opaque_pipeline; cairo_region_t *region; int n_rects; int i; if (priv->clip_region != NULL) { region = cairo_region_copy (priv->clip_region); cairo_region_intersect (region, priv->opaque_region); } else { region = cairo_region_reference (priv->opaque_region); } if (!cairo_region_is_empty (region)) { 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 (fb, opaque_pipeline, &rect, &alloc); } } cairo_region_destroy (region); } /* Now, go ahead and paint the blended parts. */ /* We have three cases: * 1) blended_region has rectangles - paint the rectangles. * 2) blended_region is empty - don't paint anything * 3) blended_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_region == NULL || !cairo_region_is_empty (blended_region)) { CoglPipeline *blended_pipeline; if (priv->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, priv->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_region != NULL) { /* 1) blended_region is not empty. Paint the rectangles. */ int i; int n_rects = cairo_region_num_rectangles (blended_region); for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (blended_region, i, &rect); if (!gdk_rectangle_intersect (&tex_rect, &rect, &rect)) continue; paint_clipped_rectangle (fb, blended_pipeline, &rect, &alloc); } } else { /* 3) blended_region is NULL. Do a full paint. */ cogl_framebuffer_draw_rectangle (fb, blended_pipeline, 0, 0, alloc.x2 - alloc.x1, alloc.y2 - alloc.y1); } } if (blended_region != NULL) cairo_region_destroy (blended_region); } static void meta_shaped_texture_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { MetaShapedTexturePrivate *priv = META_SHAPED_TEXTURE (self)->priv; guint width; if (priv->texture) width = priv->tex_width; else width = priv->fallback_width; if (min_width_p) *min_width_p = width; if (natural_width_p) *natural_width_p = width; } static void meta_shaped_texture_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { MetaShapedTexturePrivate *priv = META_SHAPED_TEXTURE (self)->priv; guint height; if (priv->texture) height = priv->tex_height; else height = priv->fallback_height; if (min_height_p) *min_height_p = height; if (natural_height_p) *natural_height_p = height; } static cairo_region_t * effective_unobscured_region (MetaShapedTexture *self) { MetaShapedTexturePrivate *priv = self->priv; ClutterActor *actor; /* Fail if we have any mapped clones. */ actor = CLUTTER_ACTOR (self); do { if (clutter_actor_has_mapped_clones (actor)) return NULL; actor = clutter_actor_get_parent (actor); } while (actor != NULL); return priv->unobscured_region; } static gboolean get_unobscured_bounds (MetaShapedTexture *self, cairo_rectangle_int_t *unobscured_bounds) { cairo_region_t *unobscured_region = effective_unobscured_region (self); if (unobscured_region) { cairo_region_get_extents (unobscured_region, unobscured_bounds); return TRUE; } else return FALSE; } static gboolean meta_shaped_texture_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { MetaShapedTexture *self = META_SHAPED_TEXTURE (actor); ClutterActorBox box; cairo_rectangle_int_t unobscured_bounds; if (!clutter_actor_has_allocation (actor)) return FALSE; clutter_actor_get_allocation_box (actor, &box); if (get_unobscured_bounds (self, &unobscured_bounds)) { box.x1 = MAX (unobscured_bounds.x, box.x1); box.x2 = MIN (unobscured_bounds.x + unobscured_bounds.width, box.x2); box.y1 = MAX (unobscured_bounds.y, box.y1); box.y2 = MIN (unobscured_bounds.y + unobscured_bounds.height, box.y2); } box.x2 = MAX (box.x2, box.x1); box.y2 = MAX (box.y2, box.y1); clutter_paint_volume_union_box (volume, &box); return TRUE; } void meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex, gboolean create_mipmaps) { MetaShapedTexturePrivate *priv; g_return_if_fail (META_IS_SHAPED_TEXTURE (stex)); priv = stex->priv; create_mipmaps = create_mipmaps != FALSE; if (create_mipmaps != priv->create_mipmaps) { CoglTexture *base_texture; priv->create_mipmaps = create_mipmaps; base_texture = create_mipmaps ? priv->texture : NULL; meta_texture_tower_set_base_texture (priv->paint_tower, base_texture); } } void meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex, CoglTexture *mask_texture) { MetaShapedTexturePrivate *priv; g_return_if_fail (META_IS_SHAPED_TEXTURE (stex)); priv = stex->priv; g_clear_pointer (&priv->mask_texture, cogl_object_unref); if (mask_texture != NULL) { priv->mask_texture = mask_texture; cogl_object_ref (priv->mask_texture); } clutter_actor_queue_redraw (CLUTTER_ACTOR (stex)); } gboolean meta_shaped_texture_is_obscured (MetaShapedTexture *self) { cairo_region_t *unobscured_region = effective_unobscured_region (self); if (unobscured_region) return cairo_region_is_empty (unobscured_region); else return FALSE; } /** * 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 * * 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) { MetaShapedTexturePrivate *priv; cairo_region_t *unobscured_region; const cairo_rectangle_int_t clip = { x, y, width, height }; priv = stex->priv; if (priv->texture == NULL) return FALSE; meta_texture_tower_update_area (priv->paint_tower, x, y, width, height); unobscured_region = effective_unobscured_region (stex); if (unobscured_region) { cairo_region_t *intersection; if (cairo_region_is_empty (unobscured_region)) return FALSE; 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 (stex), &damage_rect); cairo_region_destroy (intersection); return TRUE; } cairo_region_destroy (intersection); return FALSE; } else { clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &clip); 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)); 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) { MetaShapedTexturePrivate *priv = stex->priv; if (priv->is_y_inverted == is_y_inverted) return; meta_shaped_texture_reset_pipelines (stex); priv->is_y_inverted = is_y_inverted; } /** * meta_shaped_texture_set_snippet: (skip) */ void meta_shaped_texture_set_snippet (MetaShapedTexture *stex, CoglSnippet *snippet) { MetaShapedTexturePrivate *priv = stex->priv; if (priv->snippet == snippet) return; meta_shaped_texture_reset_pipelines (stex); g_clear_pointer (&priv->snippet, cogl_object_unref); if (snippet) priv->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->priv->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) { MetaShapedTexturePrivate *priv; g_return_if_fail (META_IS_SHAPED_TEXTURE (stex)); priv = stex->priv; if (priv->opaque_region) cairo_region_destroy (priv->opaque_region); if (opaque_region) priv->opaque_region = cairo_region_reference (opaque_region); else priv->opaque_region = NULL; } cairo_region_t * meta_shaped_texture_get_opaque_region (MetaShapedTexture *stex) { MetaShapedTexturePrivate *priv = stex->priv; return priv->opaque_region; } /** * meta_shaped_texture_get_image: * @stex: A #MetaShapedTexture * @clip: 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: (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) { CoglTexture *texture, *mask_texture; cairo_rectangle_int_t texture_rect = { 0, 0, 0, 0 }; cairo_surface_t *surface; g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), NULL); texture = COGL_TEXTURE (stex->priv->texture); if (texture == NULL) return NULL; texture_rect.width = cogl_texture_get_width (texture); texture_rect.height = cogl_texture_get_height (texture); if (clip != NULL) { /* GdkRectangle is just a typedef of cairo_rectangle_int_t, * so we can use the gdk_rectangle_* APIs on these. */ if (!gdk_rectangle_intersect (&texture_rect, clip, clip)) return NULL; } if (clip != NULL) texture = cogl_texture_new_from_sub_texture (texture, clip->x, clip->y, clip->width, 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 (clip != NULL) cogl_object_unref (texture); mask_texture = stex->priv->mask_texture; if (mask_texture != NULL) { cairo_t *cr; cairo_surface_t *mask_surface; if (clip != NULL) mask_texture = cogl_texture_new_from_sub_texture (mask_texture, clip->x, clip->y, clip->width, 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 (clip != NULL) cogl_object_unref (mask_texture); } return surface; } void meta_shaped_texture_set_fallback_size (MetaShapedTexture *self, guint fallback_width, guint fallback_height) { MetaShapedTexturePrivate *priv = self->priv; priv->fallback_width = fallback_width; priv->fallback_height = fallback_height; } static void meta_shaped_texture_cull_out (MetaCullable *cullable, cairo_region_t *unobscured_region, cairo_region_t *clip_region) { MetaShapedTexture *self = META_SHAPED_TEXTURE (cullable); MetaShapedTexturePrivate *priv = self->priv; set_unobscured_region (self, unobscured_region); set_clip_region (self, clip_region); if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (self)) == 0xff) { if (priv->opaque_region) { if (unobscured_region) cairo_region_subtract (unobscured_region, priv->opaque_region); if (clip_region) cairo_region_subtract (clip_region, priv->opaque_region); } } } static void meta_shaped_texture_reset_culling (MetaCullable *cullable) { MetaShapedTexture *self = META_SHAPED_TEXTURE (cullable); set_clip_region (self, NULL); } static void cullable_iface_init (MetaCullableInterface *iface) { iface->cull_out = meta_shaped_texture_cull_out; iface->reset_culling = meta_shaped_texture_reset_culling; } ClutterActor * meta_shaped_texture_new (void) { return g_object_new (META_TYPE_SHAPED_TEXTURE, NULL); } ukwm/src/compositor/meta-shaped-texture-private.h0000664000175000017500000000357213233511035021162 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef __META_SHAPED_TEXTURE_PRIVATE_H__ #define __META_SHAPED_TEXTURE_PRIVATE_H__ #include ClutterActor *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, guint fallback_width, guint fallback_height); gboolean meta_shaped_texture_is_obscured (MetaShapedTexture *self); cairo_region_t * meta_shaped_texture_get_opaque_region (MetaShapedTexture *stex); #endif ukwm/src/compositor/meta-module.c0000664000175000017500000001201013220600404016006 0ustar fengfeng/* -*- 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 #include #include "meta-module.h" #include enum { PROP_0, PROP_PATH, }; struct _MetaModulePrivate { GModule *lib; gchar *path; GType plugin_type; }; #define META_MODULE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), META_TYPE_MODULE, MetaModulePrivate)) G_DEFINE_TYPE (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)); g_type_class_add_private (gobject_class, sizeof (MetaModulePrivate)); } static void meta_module_init (MetaModule *self) { self->priv = META_MODULE_GET_PRIVATE (self); } GType meta_module_get_plugin_type (MetaModule *module) { MetaModulePrivate *priv = META_MODULE (module)->priv; return priv->plugin_type; } ukwm/src/compositor/meta-module.h0000664000175000017500000000345313220600404016026 0ustar fengfeng/* -*- 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 ukwm/src/compositor/clutter-utils.h0000664000175000017500000000332013220600404016426 0ustar fengfeng/* -*- 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 gboolean meta_actor_vertices_are_untransformed (ClutterVertex *verts, float widthf, float heightf, int *x_origin, int *y_origin); gboolean meta_actor_is_untransformed (ClutterActor *actor, int *x_origin, int *y_origin); gboolean meta_actor_painting_untransformed (int paint_width, int paint_height, int *x_origin, int *y_origin); #endif /* __META_CLUTTER_UTILS_H__ */ ukwm/src/compositor/meta-cullable.h0000664000175000017500000000457313220600404016330 0ustar fengfeng/* -*- 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 G_BEGIN_DECLS #define META_TYPE_CULLABLE (meta_cullable_get_type ()) #define META_CULLABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_CULLABLE, MetaCullable)) #define META_IS_CULLABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_CULLABLE)) #define META_CULLABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), META_TYPE_CULLABLE, MetaCullableInterface)) typedef struct _MetaCullable MetaCullable; typedef struct _MetaCullableInterface MetaCullableInterface; struct _MetaCullableInterface { GTypeInterface g_iface; void (* cull_out) (MetaCullable *cullable, cairo_region_t *unobscured_region, cairo_region_t *clip_region); void (* reset_culling) (MetaCullable *cullable); }; GType meta_cullable_get_type (void); void meta_cullable_cull_out (MetaCullable *cullable, cairo_region_t *unobscured_region, cairo_region_t *clip_region); 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__ */ ukwm/src/compositor/meta-feedback-actor-private.h0000664000175000017500000000557513220600404021052 0ustar fengfeng/* -*- 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 /** * MetaFeedbackActor: * * This class handles the rendering of user interaction feedback */ #define META_TYPE_FEEDBACK_ACTOR (meta_feedback_actor_get_type ()) #define META_FEEDBACK_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_FEEDBACK_ACTOR, MetaFeedbackActor)) #define META_FEEDBACK_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_FEEDBACK_ACTOR, MetaFeedbackActorClass)) #define META_IS_FEEDBACK_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_FEEDBACK_ACTOR)) #define META_IS_FEEDBACK_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_FEEDBACK_ACTOR)) #define META_FEEDBACK_ACTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_FEEDBACK_ACTOR, MetaFeedbackActorClass)) typedef struct _MetaFeedbackActor MetaFeedbackActor; typedef struct _MetaFeedbackActorClass MetaFeedbackActorClass; struct _MetaFeedbackActorClass { /*< private >*/ ClutterActorClass parent_class; }; struct _MetaFeedbackActor { ClutterActor parent; }; GType meta_feedback_actor_get_type (void); ClutterActor *meta_feedback_actor_new (int anchor_x, int anchor_y); void meta_feedback_actor_set_anchor (MetaFeedbackActor *actor, int anchor_x, int anchor_y); void meta_feedback_actor_get_anchor (MetaFeedbackActor *actor, int *anchor_x, int *anchor_y); void meta_feedback_actor_set_position (MetaFeedbackActor *self, int x, int y); void meta_feedback_actor_update (MetaFeedbackActor *self, const ClutterEvent *event); #endif /* META_FEEDBACK_ACTOR_PRIVATE_H */ ukwm/src/compositor/region-utils.c0000664000175000017500000002465413220600404016237 0ustar fengfeng/* -*- 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 "region-utils.h" #include /* 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 (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); rects = g_malloc (sizeof(cairo_rectangle_int_t) * n_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); g_free (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; } ukwm/src/compositor/meta-plugin-manager.c0000664000175000017500000002767213220600404017453 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 Intel Corp. * Copyright (c) 2017 Tianjin KYLIN Information Technology Co., Ltd. * * 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-private.h" #include "meta-plugin-manager.h" #include #include #include #include "meta-module.h" #include "window-private.h" #include "meta-close-dialog-default-private.h" #include "meta-inhibit-shortcuts-dialog-default-private.h" #include #include #include 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 ("Ukwm 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 = UKWM_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 = plugin_mgr->compositor->display; 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; 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 = plugin_mgr->compositor->display; 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 = plugin_mgr->compositor->display; 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) return klass->confirm_display_change (plugin); else return 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 = plugin_mgr->compositor->display; 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 = plugin_mgr->compositor->display; 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 = plugin_mgr->compositor->display; 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 = plugin_mgr->compositor->display; 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); } ukwm/src/compositor/meta-sync-ring.c0000664000175000017500000003724513220600404016453 0ustar fengfeng/* * 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 #include #include #include #include #include #include #include "meta-sync-ring.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); } /* fall through */ 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); } ukwm/src/compositor/meta-background-actor-private.h0000664000175000017500000000053013220600404021427 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef META_BACKGROUND_ACTOR_PRIVATE_H #define META_BACKGROUND_ACTOR_PRIVATE_H #include #include cairo_region_t *meta_background_actor_get_clip_region (MetaBackgroundActor *self); #endif /* META_BACKGROUND_ACTOR_PRIVATE_H */ ukwm/src/compositor/meta-surface-actor-wayland.c0000664000175000017500000003625313233511035020740 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "meta-surface-actor-wayland.h" #include #include #include "meta-shaped-texture-private.h" #include "backends/meta-logical-monitor.h" #include "wayland/meta-wayland-buffer.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-window-wayland.h" #include "backends/meta-backend-private.h" #include "compositor/region-utils.h" struct _MetaSurfaceActorWaylandPrivate { MetaWaylandSurface *surface; struct wl_list frame_callback_list; }; typedef struct _MetaSurfaceActorWaylandPrivate MetaSurfaceActorWaylandPrivate; G_DEFINE_TYPE_WITH_PRIVATE (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) { } static void meta_surface_actor_wayland_pre_paint (MetaSurfaceActor *actor) { } static gboolean meta_surface_actor_wayland_is_visible (MetaSurfaceActor *actor) { /* TODO: ensure that the buffer isn't NULL, implement * wayland mapping semantics */ return TRUE; } static gboolean meta_surface_actor_wayland_should_unredirect (MetaSurfaceActor *actor) { return FALSE; } static void meta_surface_actor_wayland_set_unredirected (MetaSurfaceActor *actor, gboolean unredirected) { /* Do nothing. In the future, we'll use KMS to set this * up as a hardware overlay or something. */ } static gboolean meta_surface_actor_wayland_is_unredirected (MetaSurfaceActor *actor) { return FALSE; } double meta_surface_actor_wayland_get_scale (MetaSurfaceActorWayland *self) { MetaWaylandSurface *surface = meta_surface_actor_wayland_get_surface (self); MetaWindow *window; int geometry_scale = 1; g_assert (surface); window = meta_wayland_surface_get_toplevel_window (surface); if (!meta_is_stage_views_scaled ()) { /* XXX: We do not handle x11 clients yet */ if (window && window->client_type != META_WINDOW_CLIENT_TYPE_X11) geometry_scale = meta_window_wayland_get_geometry_scale (window); } return (double) geometry_scale / (double) surface->scale; } static void logical_to_actor_position (MetaSurfaceActorWayland *self, int *x, int *y) { MetaWaylandSurface *surface = meta_surface_actor_wayland_get_surface (self); MetaWindow *toplevel_window; int geometry_scale = 1; g_assert (surface); toplevel_window = meta_wayland_surface_get_toplevel_window (surface); if (toplevel_window) geometry_scale = meta_window_wayland_get_geometry_scale (toplevel_window); *x = *x * geometry_scale; *y = *y * geometry_scale; } /* Convert the current actor state to the corresponding subsurface rectangle * in logical pixel coordinate space. */ void meta_surface_actor_wayland_get_subsurface_rect (MetaSurfaceActorWayland *self, MetaRectangle *rect) { MetaWaylandSurface *surface = meta_surface_actor_wayland_get_surface (self); MetaWaylandBuffer *buffer = meta_wayland_surface_get_buffer (surface); CoglTexture *texture; MetaWindow *toplevel_window; int geometry_scale; float x, y; g_assert (surface); texture = buffer->texture; toplevel_window = meta_wayland_surface_get_toplevel_window (surface); geometry_scale = meta_window_wayland_get_geometry_scale (toplevel_window); clutter_actor_get_position (CLUTTER_ACTOR (self), &x, &y); *rect = (MetaRectangle) { .x = x / geometry_scale, .y = y / geometry_scale, .width = cogl_texture_get_width (texture) / surface->scale, .height = cogl_texture_get_height (texture) / surface->scale, }; } void meta_surface_actor_wayland_sync_subsurface_state (MetaSurfaceActorWayland *self) { MetaWaylandSurface *surface = meta_surface_actor_wayland_get_surface (self); MetaWindow *window; int x = surface->offset_x + surface->sub.x; int y = surface->offset_y + surface->sub.y; g_assert (surface); window = meta_wayland_surface_get_toplevel_window (surface); if (window && window->client_type == META_WINDOW_CLIENT_TYPE_X11) { /* Bail directly if this is part of a Xwayland window and warn * if there happen to be offsets anyway since that is not supposed * to happen. */ g_warn_if_fail (x == 0 && y == 0); return; } logical_to_actor_position (self, &x, &y); clutter_actor_set_position (CLUTTER_ACTOR (self), x, y); } void meta_surface_actor_wayland_sync_state (MetaSurfaceActorWayland *self) { MetaWaylandSurface *surface = meta_surface_actor_wayland_get_surface (self); MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self)); double texture_scale; g_assert (surface); /* Given the surface's window type and what output the surface actor has the * largest region, scale the actor with the determined scale. */ texture_scale = meta_surface_actor_wayland_get_scale (self); /* Actor scale. */ clutter_actor_set_scale (CLUTTER_ACTOR (stex), texture_scale, texture_scale); /* Input region */ if (surface->input_region) { cairo_region_t *scaled_input_region; int region_scale; /* The input region from the Wayland surface is in the Wayland surface * coordinate space, while the surface actor input region is in the * physical pixel coordinate space. */ region_scale = (int)(surface->scale * texture_scale); scaled_input_region = meta_region_scale (surface->input_region, region_scale); meta_surface_actor_set_input_region (META_SURFACE_ACTOR (self), scaled_input_region); cairo_region_destroy (scaled_input_region); } else { meta_surface_actor_set_input_region (META_SURFACE_ACTOR (self), NULL); } /* Opaque region */ if (surface->opaque_region) { cairo_region_t *scaled_opaque_region; /* The opaque region from the Wayland surface is in Wayland surface * coordinate space, while the surface actor opaque region is in the * same coordinate space as the unscaled buffer texture. */ scaled_opaque_region = meta_region_scale (surface->opaque_region, surface->scale); meta_surface_actor_set_opaque_region (META_SURFACE_ACTOR (self), scaled_opaque_region); cairo_region_destroy (scaled_opaque_region); } else { meta_surface_actor_set_opaque_region (META_SURFACE_ACTOR (self), NULL); } meta_surface_actor_wayland_sync_subsurface_state (self); } void meta_surface_actor_wayland_sync_state_recursive (MetaSurfaceActorWayland *self) { MetaWaylandSurface *surface = meta_surface_actor_wayland_get_surface (self); MetaWindow *window; GList *iter; g_assert (surface); window = meta_wayland_surface_get_toplevel_window (surface); meta_surface_actor_wayland_sync_state (self); if (window && window->client_type != META_WINDOW_CLIENT_TYPE_X11) { for (iter = surface->subsurfaces; iter != NULL; iter = iter->next) { MetaWaylandSurface *subsurf = iter->data; meta_surface_actor_wayland_sync_state_recursive ( META_SURFACE_ACTOR_WAYLAND (subsurf->surface_actor)); } } } gboolean meta_surface_actor_wayland_is_on_monitor (MetaSurfaceActorWayland *self, MetaLogicalMonitor *logical_monitor) { float x, y, width, height; cairo_rectangle_int_t actor_rect; cairo_region_t *region; gboolean is_on_monitor; clutter_actor_get_transformed_position (CLUTTER_ACTOR (self), &x, &y); clutter_actor_get_transformed_size (CLUTTER_ACTOR (self), &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); cairo_region_intersect_rectangle (region, &((cairo_rectangle_int_t) { .x = logical_monitor->rect.x, .y = logical_monitor->rect.y, .width = logical_monitor->rect.width, .height = logical_monitor->rect.height, })); is_on_monitor = !cairo_region_is_empty (region); cairo_region_destroy (region); return is_on_monitor; } void meta_surface_actor_wayland_add_frame_callbacks (MetaSurfaceActorWayland *self, struct wl_list *frame_callbacks) { MetaSurfaceActorWaylandPrivate *priv = meta_surface_actor_wayland_get_instance_private (self); wl_list_insert_list (&priv->frame_callback_list, frame_callbacks); } static MetaWindow * meta_surface_actor_wayland_get_window (MetaSurfaceActor *actor) { MetaSurfaceActorWayland *self = META_SURFACE_ACTOR_WAYLAND (actor); MetaWaylandSurface *surface = meta_surface_actor_wayland_get_surface (self); if (!surface) return NULL; return surface->window; } static void meta_surface_actor_wayland_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { MetaSurfaceActorWayland *self = META_SURFACE_ACTOR_WAYLAND (actor); MetaWaylandSurface *surface = meta_surface_actor_wayland_get_surface (self); MetaShapedTexture *stex; double scale; if (surface) scale = meta_surface_actor_wayland_get_scale (self); else scale = 1.0; stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self)); clutter_actor_get_preferred_width (CLUTTER_ACTOR (stex), for_height, min_width_p, natural_width_p); if (min_width_p) *min_width_p *= scale; if (natural_width_p) *natural_width_p *= scale; } static void meta_surface_actor_wayland_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { MetaSurfaceActorWayland *self = META_SURFACE_ACTOR_WAYLAND (actor); MetaWaylandSurface *surface = meta_surface_actor_wayland_get_surface (self); MetaShapedTexture *stex; double scale; if (surface) scale = meta_surface_actor_wayland_get_scale (self); else scale = 1.0; stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self)); clutter_actor_get_preferred_height (CLUTTER_ACTOR (stex), for_width, min_height_p, natural_height_p); if (min_height_p) *min_height_p *= scale; if (natural_height_p) *natural_height_p *= scale; } static void meta_surface_actor_wayland_paint (ClutterActor *actor) { MetaSurfaceActorWayland *self = META_SURFACE_ACTOR_WAYLAND (actor); MetaSurfaceActorWaylandPrivate *priv = meta_surface_actor_wayland_get_instance_private (self); if (priv->surface) { MetaWaylandCompositor *compositor = priv->surface->compositor; wl_list_insert_list (&compositor->frame_callbacks, &priv->frame_callback_list); wl_list_init (&priv->frame_callback_list); } CLUTTER_ACTOR_CLASS (meta_surface_actor_wayland_parent_class)->paint (actor); } static void meta_surface_actor_wayland_dispose (GObject *object) { MetaSurfaceActorWayland *self = META_SURFACE_ACTOR_WAYLAND (object); MetaSurfaceActorWaylandPrivate *priv = meta_surface_actor_wayland_get_instance_private (self); MetaWaylandFrameCallback *cb, *next; MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self)); meta_shaped_texture_set_texture (stex, NULL); if (priv->surface) { g_object_remove_weak_pointer (G_OBJECT (priv->surface), (gpointer *) &priv->surface); priv->surface = NULL; } wl_list_for_each_safe (cb, next, &priv->frame_callback_list, link) wl_resource_destroy (cb->resource); 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); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); actor_class->get_preferred_width = meta_surface_actor_wayland_get_preferred_width; actor_class->get_preferred_height = meta_surface_actor_wayland_get_preferred_height; actor_class->paint = meta_surface_actor_wayland_paint; 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_visible = meta_surface_actor_wayland_is_visible; surface_actor_class->should_unredirect = meta_surface_actor_wayland_should_unredirect; surface_actor_class->set_unredirected = meta_surface_actor_wayland_set_unredirected; surface_actor_class->is_unredirected = meta_surface_actor_wayland_is_unredirected; surface_actor_class->get_window = meta_surface_actor_wayland_get_window; 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); MetaSurfaceActorWaylandPrivate *priv = meta_surface_actor_wayland_get_instance_private (self); g_assert (meta_is_wayland_compositor ()); wl_list_init (&priv->frame_callback_list); priv->surface = surface; g_object_add_weak_pointer (G_OBJECT (priv->surface), (gpointer *) &priv->surface); return META_SURFACE_ACTOR (self); } MetaWaylandSurface * meta_surface_actor_wayland_get_surface (MetaSurfaceActorWayland *self) { MetaSurfaceActorWaylandPrivate *priv = meta_surface_actor_wayland_get_instance_private (self); return priv->surface; } ukwm/src/compositor/meta-window-actor-private.h0000664000175000017500000000550413220600404020625 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef META_WINDOW_ACTOR_PRIVATE_H #define META_WINDOW_ACTOR_PRIVATE_H #include #include #include #include "meta-surface-actor.h" #include "meta-plugin-manager.h" MetaWindowActor *meta_window_actor_new (MetaWindow *window); void meta_window_actor_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_process_x11_damage (MetaWindowActor *self, XDamageNotifyEvent *event); 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); void meta_window_actor_invalidate_shadow (MetaWindowActor *self); void meta_window_actor_get_shape_bounds (MetaWindowActor *self, cairo_rectangle_int_t *bounds); gboolean meta_window_actor_should_unredirect (MetaWindowActor *self); void meta_window_actor_set_unredirected (MetaWindowActor *self, gboolean unredirected); gboolean meta_window_actor_effect_in_progress (MetaWindowActor *self); void meta_window_actor_sync_actor_geometry (MetaWindowActor *self, gboolean did_placement); void meta_window_actor_update_shape (MetaWindowActor *self); 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_update_surface (MetaWindowActor *self); #endif /* META_WINDOW_ACTOR_PRIVATE_H */ ukwm/src/compositor/meta-background-private.h0000664000175000017500000000104313220600404020321 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef META_BACKGROUND_PRIVATE_H #define META_BACKGROUND_PRIVATE_H #include #include "meta-background-private.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 */ ukwm/src/compositor/meta-feedback-actor.c0000664000175000017500000001556213220600404017372 0ustar fengfeng/* -*- 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 #include "display-private.h" #include "compositor-private.h" #include "meta-feedback-actor-private.h" enum { PROP_ANCHOR_X = 1, PROP_ANCHOR_Y }; typedef struct _MetaFeedbackActorPrivate MetaFeedbackActorPrivate; struct _MetaFeedbackActorPrivate { int anchor_x; int anchor_y; int pos_x; int pos_y; }; G_DEFINE_TYPE_WITH_PRIVATE (MetaFeedbackActor, meta_feedback_actor, CLUTTER_TYPE_ACTOR) static void meta_feedback_actor_constructed (GObject *object) { MetaDisplay *display; display = meta_get_display (); clutter_actor_add_child (display->compositor->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->pos_y - priv->anchor_y); } 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_int (value, priv->anchor_x); break; case PROP_ANCHOR_Y: g_value_set_int (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_int ("anchor-x", "Anchor X", "The X axis of the anchor point", 0, G_MAXINT, 0, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ANCHOR_X, pspec); pspec = g_param_spec_int ("anchor-y", "Anchor Y", "The Y axis of the anchor point", 0, G_MAXINT, 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 (int anchor_x, int 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, int anchor_x, int 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, int *anchor_x, int *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, int x, int 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) { ClutterPoint 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); } ukwm/src/compositor/meta-background-group.c0000664000175000017500000000344213220600404020003 0ustar fengfeng/* -*- 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 #include #include "meta-cullable.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); } ukwm/src/compositor/meta-texture-tower.h0000664000175000017500000000545013220600404017376 0ustar fengfeng/* -*- 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 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); G_END_DECLS #endif /* __META_TEXTURE_TOWER_H__ */ ukwm/src/compositor/region-utils.h0000664000175000017500000000670313220600404016237 0ustar fengfeng/* -*- 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 /** * 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_make_border_region (cairo_region_t *region, int x_amount, int y_amount, gboolean flip); #endif /* __META_REGION_UTILS_H__ */ ukwm/src/compositor/meta-window-actor.c0000664000175000017500000017676213220600404017167 0ustar fengfeng/* -*- 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 */ #include #include #include #include #include /* for gdk_rectangle_union() */ #include #include #include #include "frame.h" #include #include #include #include #include "clutter/clutter-ukwm.h" #include "compositor-private.h" #include "meta-shaped-texture-private.h" #include "meta-window-actor-private.h" #include "meta-texture-rectangle.h" #include "region-utils.h" #include "backends/meta-logical-monitor.h" #include "meta-monitor-manager-private.h" #include "meta-cullable.h" #include "meta-surface-actor.h" #include "meta-surface-actor-x11.h" #ifdef HAVE_WAYLAND #include "meta-surface-actor-wayland.h" #include "wayland/meta-wayland-surface.h" #endif typedef enum { INITIALLY_FROZEN, DRAWING_FIRST_FRAME, EMITTED_FIRST_FRAME } FirstFrameState; struct _MetaWindowActorPrivate { MetaWindow *window; MetaCompositor *compositor; MetaSurfaceActor *surface; /* 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; /* Extracted size-invariant shape used for shadows */ MetaWindowShape *shadow_shape; char * shadow_class; MetaShadowMode shadow_mode; guint send_frame_messages_timer; gint64 frame_drawn_time; guint repaint_scheduled_id; guint size_changed_id; /* * 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; /* List of FrameData for recent frames */ GList *frames; guint freeze_count; guint visible : 1; guint disposed : 1; /* If set, the client needs to be sent a _NET_WM_FRAME_DRAWN * client message for one or more messages in ->frames */ guint needs_frame_drawn : 1; guint repaint_scheduled : 1; guint needs_reshape : 1; guint recompute_focused_shadow : 1; guint recompute_unfocused_shadow : 1; guint needs_destroy : 1; guint updates_frozen : 1; guint first_frame_state : 2; /* FirstFrameState */ }; typedef struct _FrameData FrameData; /* 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. */ struct _FrameData { guint64 sync_request_serial; int64_t frame_counter; gint64 frame_drawn_time; }; enum { FIRST_FRAME, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; enum { PROP_META_WINDOW = 1, PROP_SHADOW_MODE, PROP_SHADOW_CLASS }; static void meta_window_actor_dispose (GObject *object); static void meta_window_actor_finalize (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_paint (ClutterActor *actor); static gboolean meta_window_actor_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume); static gboolean meta_window_actor_has_shadow (MetaWindowActor *self); static void meta_window_actor_handle_updates (MetaWindowActor *self); static void check_needs_reshape (MetaWindowActor *self); static void do_send_frame_drawn (MetaWindowActor *self, FrameData *frame); static void do_send_frame_timings (MetaWindowActor *self, FrameData *frame, gint refresh_interval, gint64 presentation_time); static void cullable_iface_init (MetaCullableInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaWindowActor, meta_window_actor, CLUTTER_TYPE_ACTOR, G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init)); static void frame_data_free (FrameData *frame) { g_slice_free (FrameData, frame); } static void meta_window_actor_class_init (MetaWindowActorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); GParamSpec *pspec; g_type_class_add_private (klass, sizeof (MetaWindowActorPrivate)); object_class->dispose = meta_window_actor_dispose; object_class->finalize = meta_window_actor_finalize; 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; actor_class->paint = meta_window_actor_paint; actor_class->get_paint_volume = meta_window_actor_get_paint_volume; /** * 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 Ukwm * 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); 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); 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); } static void meta_window_actor_init (MetaWindowActor *self) { MetaWindowActorPrivate *priv; priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, META_TYPE_WINDOW_ACTOR, MetaWindowActorPrivate); priv->shadow_class = NULL; } static void window_appears_focused_notify (MetaWindow *mw, GParamSpec *arg1, gpointer data) { clutter_actor_queue_redraw (CLUTTER_ACTOR (data)); } static void surface_size_changed (MetaSurfaceActor *actor, gpointer user_data) { MetaWindowActor *self = META_WINDOW_ACTOR (user_data); meta_window_actor_update_shape (self); } static void surface_repaint_scheduled (MetaSurfaceActor *actor, gpointer user_data) { MetaWindowActor *self = META_WINDOW_ACTOR (user_data); MetaWindowActorPrivate *priv = self->priv; priv->repaint_scheduled = TRUE; } static gboolean is_argb32 (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; /* assume we're argb until we get the window (because in practice we're drawing nothing, so we're fully transparent) */ if (priv->surface) return meta_surface_actor_is_argb32 (priv->surface); else return TRUE; } static gboolean is_non_opaque (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; MetaWindow *window = priv->window; return is_argb32 (self) || (window->opacity != 0xFF); } static gboolean is_frozen (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; return priv->surface == NULL || priv->freeze_count > 0; } static void meta_window_actor_freeze (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; if (priv->freeze_count == 0 && priv->surface) meta_surface_actor_set_frozen (priv->surface, TRUE); priv->freeze_count ++; } static void meta_window_actor_sync_thawed_state (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; if (priv->first_frame_state == INITIALLY_FROZEN) priv->first_frame_state = DRAWING_FIRST_FRAME; if (priv->surface) meta_surface_actor_set_frozen (priv->surface, 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 = self->priv; 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 (is_frozen (self)) return; meta_window_actor_sync_thawed_state (self); /* We do this now since we might be going right back into the * frozen state */ meta_window_actor_handle_updates (self); } static void set_surface (MetaWindowActor *self, MetaSurfaceActor *surface) { MetaWindowActorPrivate *priv = self->priv; if (priv->surface) { g_signal_handler_disconnect (priv->surface, priv->repaint_scheduled_id); g_signal_handler_disconnect (priv->surface, priv->size_changed_id); priv->repaint_scheduled_id = 0; clutter_actor_remove_child (CLUTTER_ACTOR (self), CLUTTER_ACTOR (priv->surface)); g_object_unref (priv->surface); } priv->surface = surface; if (priv->surface) { g_object_ref_sink (priv->surface); priv->repaint_scheduled_id = g_signal_connect (priv->surface, "repaint-scheduled", G_CALLBACK (surface_repaint_scheduled), self); priv->size_changed_id = g_signal_connect (priv->surface, "size-changed", G_CALLBACK (surface_size_changed), self); clutter_actor_add_child (CLUTTER_ACTOR (self), CLUTTER_ACTOR (priv->surface)); meta_window_actor_update_shape (self); if (is_frozen (self)) meta_surface_actor_set_frozen (priv->surface, TRUE); else meta_window_actor_sync_thawed_state (self); } } void meta_window_actor_update_surface (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; MetaWindow *window = priv->window; MetaSurfaceActor *surface_actor; #ifdef HAVE_WAYLAND if (window->surface) surface_actor = window->surface->surface_actor; else #endif if (!meta_is_wayland_compositor ()) surface_actor = meta_surface_actor_x11_new (window); else surface_actor = NULL; set_surface (self, surface_actor); } static void meta_window_actor_constructed (GObject *object) { MetaWindowActor *self = META_WINDOW_ACTOR (object); MetaWindowActorPrivate *priv = self->priv; MetaWindow *window = priv->window; priv->compositor = window->display->compositor; meta_window_actor_update_surface (self); meta_window_actor_update_opacity (self); /* Start off with an empty shape region to maintain the invariant * that it's always set */ priv->shape_region = cairo_region_create (); } static void meta_window_actor_dispose (GObject *object) { MetaWindowActor *self = META_WINDOW_ACTOR (object); MetaWindowActorPrivate *priv = self->priv; MetaCompositor *compositor = priv->compositor; if (priv->disposed) return; priv->disposed = TRUE; if (priv->send_frame_messages_timer != 0) { g_source_remove (priv->send_frame_messages_timer); priv->send_frame_messages_timer = 0; } g_clear_pointer (&priv->shape_region, cairo_region_destroy); g_clear_pointer (&priv->shadow_clip, cairo_region_destroy); g_clear_pointer (&priv->shadow_class, g_free); g_clear_pointer (&priv->focused_shadow, meta_shadow_unref); g_clear_pointer (&priv->unfocused_shadow, meta_shadow_unref); g_clear_pointer (&priv->shadow_shape, meta_window_shape_unref); compositor->windows = g_list_remove (compositor->windows, (gconstpointer) self); g_clear_object (&priv->window); set_surface (self, NULL); G_OBJECT_CLASS (meta_window_actor_parent_class)->dispose (object); } static void meta_window_actor_finalize (GObject *object) { MetaWindowActor *self = META_WINDOW_ACTOR (object); MetaWindowActorPrivate *priv = self->priv; g_list_free_full (priv->frames, (GDestroyNotify) frame_data_free); G_OBJECT_CLASS (meta_window_actor_parent_class)->finalize (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 = self->priv; 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; case PROP_SHADOW_MODE: { MetaShadowMode newv = g_value_get_enum (value); if (newv == priv->shadow_mode) return; priv->shadow_mode = newv; meta_window_actor_invalidate_shadow (self); } break; case PROP_SHADOW_CLASS: { const char *newv = g_value_get_string (value); if (g_strcmp0 (newv, priv->shadow_class) == 0) return; g_free (priv->shadow_class); priv->shadow_class = g_strdup (newv); meta_window_actor_invalidate_shadow (self); } 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) { MetaWindowActorPrivate *priv = META_WINDOW_ACTOR (object)->priv; switch (prop_id) { case PROP_META_WINDOW: g_value_set_object (value, priv->window); break; case PROP_SHADOW_MODE: g_value_set_enum (value, priv->shadow_mode); break; case PROP_SHADOW_CLASS: g_value_set_string (value, priv->shadow_class); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static const char * meta_window_actor_get_shadow_class (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; if (priv->shadow_class != NULL) return priv->shadow_class; else { MetaWindowType window_type = meta_window_get_window_type (priv->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 = meta_window_get_frame_type (priv->window); return meta_frame_type_to_string (frame_type); } } } } static void meta_window_actor_get_shadow_params (MetaWindowActor *self, gboolean appears_focused, MetaShadowParams *params) { const char *shadow_class = meta_window_actor_get_shadow_class (self); meta_shadow_factory_get_params (meta_shadow_factory_get_default (), shadow_class, appears_focused, params); } void meta_window_actor_get_shape_bounds (MetaWindowActor *self, cairo_rectangle_int_t *bounds) { MetaWindowActorPrivate *priv = self->priv; cairo_region_get_extents (priv->shape_region, bounds); } static void meta_window_actor_get_shadow_bounds (MetaWindowActor *self, gboolean appears_focused, cairo_rectangle_int_t *bounds) { MetaWindowActorPrivate *priv = self->priv; MetaShadow *shadow = appears_focused ? priv->focused_shadow : priv->unfocused_shadow; cairo_rectangle_int_t shape_bounds; MetaShadowParams params; meta_window_actor_get_shape_bounds (self, &shape_bounds); meta_window_actor_get_shadow_params (self, 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 (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; return is_non_opaque (self) && priv->window->frame; } static void assign_frame_counter_to_frames (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; MetaCompositor *compositor = priv->compositor; ClutterStage *stage = CLUTTER_STAGE (compositor->stage); 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 (priv->send_frame_messages_timer != 0) return; for (l = priv->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_paint (ClutterActor *actor) { MetaWindowActor *self = META_WINDOW_ACTOR (actor); MetaWindowActorPrivate *priv = self->priv; gboolean appears_focused = meta_window_appears_focused (priv->window); MetaShadow *shadow = appears_focused ? priv->focused_shadow : priv->unfocused_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 (priv->send_frame_messages_timer != 0) { g_source_remove (priv->send_frame_messages_timer); priv->send_frame_messages_timer = 0; assign_frame_counter_to_frames (self); } if (shadow != NULL) { MetaShadowParams params; cairo_rectangle_int_t shape_bounds; cairo_region_t *clip = priv->shadow_clip; MetaWindow *window = priv->window; meta_window_actor_get_shape_bounds (self, &shape_bounds); meta_window_actor_get_shadow_params (self, appears_focused, ¶ms); /* The frame bounds are already subtracted from priv->shadow_clip * if that exists. */ if (!clip && clip_shadow_under_window (self)) { cairo_region_t *frame_bounds = meta_window_get_frame_bounds (priv->window); cairo_rectangle_int_t bounds; meta_window_actor_get_shadow_bounds (self, appears_focused, &bounds); clip = cairo_region_create_rectangle (&bounds); cairo_region_subtract (clip, frame_bounds); } meta_shadow_paint (shadow, 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 (self)); /* clip_strictly - not just as an optimization */ if (clip && clip != priv->shadow_clip) cairo_region_destroy (clip); } CLUTTER_ACTOR_CLASS (meta_window_actor_parent_class)->paint (actor); } static gboolean meta_window_actor_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { MetaWindowActor *self = META_WINDOW_ACTOR (actor); MetaWindowActorPrivate *priv = self->priv; gboolean appears_focused = meta_window_appears_focused (priv->window); /* The paint volume is computed before paint functions are called * so our bounds might not be updated yet. Force an update. */ meta_window_actor_handle_updates (self); if (appears_focused ? priv->focused_shadow : priv->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. */ meta_window_actor_get_shadow_bounds (self, 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); } if (priv->surface) { const ClutterPaintVolume *child_volume; child_volume = clutter_actor_get_transformed_paint_volume (CLUTTER_ACTOR (priv->surface), actor); if (!child_volume) return FALSE; clutter_paint_volume_union (volume, child_volume); } return TRUE; } static gboolean meta_window_actor_has_shadow (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; if (priv->shadow_mode == META_SHADOW_MODE_FORCED_OFF) return FALSE; if (priv->shadow_mode == META_SHADOW_MODE_FORCED_ON) return TRUE; /* Leaving out shadows for 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 (priv->window) == META_MAXIMIZE_BOTH) || meta_window_is_fullscreen (priv->window)) return FALSE; /* * If we have two snap-tiled windows, we don't want the shadow to obstruct * the other window. */ if (meta_window_get_tile_match (priv->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 (priv->window)) return TRUE; /* * Do not add shadows to non-opaque (ARGB32) windows, as we can't easily * generate shadows for them. */ if (is_non_opaque (self)) 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 (priv->window->has_custom_frame_extents) return FALSE; /* * Generate shadows for all other windows. */ return TRUE; } /** * 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) { return self->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 */ ClutterActor * meta_window_actor_get_texture (MetaWindowActor *self) { if (self->priv->surface) return CLUTTER_ACTOR (meta_surface_actor_get_texture (self->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) { return self->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) { return self->priv->disposed || self->priv->needs_destroy; } static gboolean send_frame_messages_timeout (gpointer data) { MetaWindowActor *self = (MetaWindowActor *) data; MetaWindowActorPrivate *priv = self->priv; GList *l; for (l = priv->frames; l;) { GList *l_next = l->next; FrameData *frame = l->data; if (frame->frame_counter == -1) { do_send_frame_drawn (self, frame); do_send_frame_timings (self, frame, 0, 0); priv->frames = g_list_delete_link (priv->frames, l); frame_data_free (frame); } l = l_next; } priv->needs_frame_drawn = FALSE; priv->send_frame_messages_timer = 0; return FALSE; } static void queue_send_frame_messages_timeout (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; if (priv->send_frame_messages_timer != 0) return; MetaDisplay *display = meta_window_get_display (priv->window); gint64 current_time = meta_compositor_monotonic_time_to_server_time (display, g_get_monotonic_time ()); MetaMonitorManager *monitor_manager = meta_monitor_manager_get (); MetaWindow *window = priv->window; MetaOutput *outputs; guint n_outputs, i; float refresh_rate = 60.0f; gint interval, offset; outputs = meta_monitor_manager_get_outputs (monitor_manager, &n_outputs); for (i = 0; i < n_outputs; i++) { if (outputs[i].winsys_id == window->monitor->winsys_id && outputs[i].crtc) { refresh_rate = outputs[i].crtc->current_mode->refresh_rate; break; } } interval = (int)(1000000 / refresh_rate) * 6; offset = MAX (0, priv->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. */ priv->send_frame_messages_timer = g_timeout_add_full (META_PRIORITY_REDRAW, offset, send_frame_messages_timeout, self, NULL); g_source_set_name_by_id (priv->send_frame_messages_timer, "[ukwm] send_frame_messages_timeout"); } void meta_window_actor_queue_frame_drawn (MetaWindowActor *self, gboolean no_delay_frame) { MetaWindowActorPrivate *priv = self->priv; FrameData *frame; if (meta_window_actor_is_destroyed (self)) return; frame = g_slice_new0 (FrameData); frame->frame_counter = -1; priv->needs_frame_drawn = TRUE; frame->sync_request_serial = priv->window->sync_request_serial; priv->frames = g_list_prepend (priv->frames, frame); if (no_delay_frame) { ClutterActor *stage = clutter_actor_get_stage (CLUTTER_ACTOR (self)); clutter_stage_skip_sync_delay (CLUTTER_STAGE (stage)); } if (!priv->repaint_scheduled) { gboolean is_obscured; if (priv->surface) is_obscured = meta_surface_actor_is_obscured (priv->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 (self); } else { if (priv->surface) { const cairo_rectangle_int_t clip = { 0, 0, 1, 1 }; clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (priv->surface), &clip); priv->repaint_scheduled = TRUE; } } } } gboolean meta_window_actor_effect_in_progress (MetaWindowActor *self) { return (self->priv->minimize_in_progress || self->priv->size_change_in_progress || self->priv->map_in_progress || self->priv->destroy_in_progress); } static gboolean is_freeze_thaw_effect (MetaPluginEffect event) { switch (event) { case META_PLUGIN_DESTROY: case META_PLUGIN_SIZE_CHANGE: return TRUE; break; default: return FALSE; } } static gboolean start_simple_effect (MetaWindowActor *self, MetaPluginEffect event) { MetaWindowActorPrivate *priv = self->priv; MetaCompositor *compositor = priv->compositor; gint *counter = NULL; gboolean use_freeze_thaw = FALSE; g_assert (compositor->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 (compositor->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 = self->priv; if (priv->needs_destroy) { clutter_actor_destroy (CLUTTER_ACTOR (self)); return; } 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 = self->priv; 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); } gboolean meta_window_actor_should_unredirect (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; if (!meta_window_actor_is_destroyed (self) && priv->surface) return meta_surface_actor_should_unredirect (priv->surface); else return FALSE; } void meta_window_actor_set_unredirected (MetaWindowActor *self, gboolean unredirected) { MetaWindowActorPrivate *priv = self->priv; g_assert(priv->surface); /* because otherwise should_unredirect() is FALSE */ meta_surface_actor_set_unredirected (priv->surface, unredirected); } void meta_window_actor_destroy (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; MetaWindow *window = priv->window; MetaWindowType window_type = meta_window_get_window_type (window); meta_window_set_compositor_private (window, NULL); if (priv->send_frame_messages_timer != 0) { g_source_remove (priv->send_frame_messages_timer); priv->send_frame_messages_timer = 0; } 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)); } void meta_window_actor_sync_actor_geometry (MetaWindowActor *self, gboolean did_placement) { MetaWindowActorPrivate *priv = self->priv; MetaRectangle window_rect; 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 (is_frozen (self) && !did_placement) return; if (meta_window_actor_effect_in_progress (self)) return; clutter_actor_set_position (CLUTTER_ACTOR (self), window_rect.x, window_rect.y); clutter_actor_set_size (CLUTTER_ACTOR (self), window_rect.width, window_rect.height); } void meta_window_actor_show (MetaWindowActor *self, MetaCompEffect effect) { MetaWindowActorPrivate *priv = self->priv; MetaCompositor *compositor = priv->compositor; MetaPluginEffect event; g_return_if_fail (!priv->visible); self->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 (compositor->switch_workspace_in_progress || !start_simple_effect (self, event)) { clutter_actor_show (CLUTTER_ACTOR (self)); } } void meta_window_actor_hide (MetaWindowActor *self, MetaCompEffect effect) { MetaWindowActorPrivate *priv = self->priv; 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 (compositor->switch_workspace_in_progress) 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 = self->priv; MetaCompositor *compositor = priv->compositor; self->priv->size_change_in_progress++; meta_window_actor_freeze (self); if (!meta_plugin_manager_event_size_change (compositor->plugin_mgr, self, which_change, old_frame_rect, old_buffer_rect)) { self->priv->size_change_in_progress--; meta_window_actor_thaw (self); } } MetaWindowActor * meta_window_actor_new (MetaWindow *window) { MetaDisplay *display = meta_window_get_display (window); MetaCompositor *compositor = display->compositor; MetaWindowActor *self; MetaWindowActorPrivate *priv; ClutterActor *window_group; self = g_object_new (META_TYPE_WINDOW_ACTOR, "meta-window", window, NULL); priv = self->priv; meta_window_actor_sync_updates_frozen (self); if (is_frozen (self)) priv->first_frame_state = INITIALLY_FROZEN; else priv->first_frame_state = DRAWING_FIRST_FRAME; /* 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 (priv->window->extended_sync_request_counter && !priv->updates_frozen) meta_window_actor_queue_frame_drawn (self, FALSE); meta_window_actor_sync_actor_geometry (self, priv->window->placed); /* Hang our compositor window state off the MetaWindow for fast retrieval */ meta_window_set_compositor_private (window, G_OBJECT (self)); if (window->layer == META_LAYER_OVERRIDE_REDIRECT) window_group = compositor->top_window_group; else window_group = compositor->window_group; clutter_actor_add_child (window_group, CLUTTER_ACTOR (self)); clutter_actor_hide (CLUTTER_ACTOR (self)); /* Initial position in the stack is arbitrary; stacking will be synced * before we first paint. */ compositor->windows = g_list_append (compositor->windows, self); return 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 /** * meta_window_actor_set_clip_region_beneath: * @self: a #MetaWindowActor * @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 meta_window_actor_set_clip_region_beneath (MetaWindowActor *self, cairo_region_t *beneath_region) { MetaWindowActorPrivate *priv = self->priv; gboolean appears_focused = meta_window_appears_focused (priv->window); if (appears_focused ? priv->focused_shadow : priv->unfocused_shadow) { g_clear_pointer (&priv->shadow_clip, cairo_region_destroy); if (beneath_region) { priv->shadow_clip = cairo_region_copy (beneath_region); if (clip_shadow_under_window (self)) { cairo_region_t *frame_bounds = meta_window_get_frame_bounds (priv->window); cairo_region_subtract (priv->shadow_clip, frame_bounds); } } else priv->shadow_clip = NULL; } } static void meta_window_actor_cull_out (MetaCullable *cullable, cairo_region_t *unobscured_region, cairo_region_t *clip_region) { MetaWindowActor *self = META_WINDOW_ACTOR (cullable); meta_cullable_cull_out_children (cullable, unobscured_region, clip_region); meta_window_actor_set_clip_region_beneath (self, clip_region); } static void meta_window_actor_reset_culling (MetaCullable *cullable) { MetaWindowActor *self = META_WINDOW_ACTOR (cullable); MetaWindowActorPrivate *priv = self->priv; g_clear_pointer (&priv->shadow_clip, cairo_region_destroy); 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; } static void check_needs_shadow (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; MetaShadow *old_shadow = NULL; MetaShadow **shadow_location; gboolean recompute_shadow; gboolean should_have_shadow; gboolean appears_focused; /* Calling meta_window_actor_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 priv->recompute_shadow. */ should_have_shadow = meta_window_actor_has_shadow (self); appears_focused = meta_window_appears_focused (priv->window); if (appears_focused) { recompute_shadow = priv->recompute_focused_shadow; priv->recompute_focused_shadow = FALSE; shadow_location = &priv->focused_shadow; } else { recompute_shadow = priv->recompute_unfocused_shadow; priv->recompute_unfocused_shadow = FALSE; shadow_location = &priv->unfocused_shadow; } if (!should_have_shadow || recompute_shadow) { if (*shadow_location != NULL) { old_shadow = *shadow_location; *shadow_location = NULL; } } if (*shadow_location == NULL && should_have_shadow) { if (priv->shadow_shape == NULL) priv->shadow_shape = meta_window_shape_new (priv->shape_region); MetaShadowFactory *factory = meta_shadow_factory_get_default (); const char *shadow_class = meta_window_actor_get_shadow_class (self); cairo_rectangle_int_t shape_bounds; meta_window_actor_get_shape_bounds (self, &shape_bounds); *shadow_location = meta_shadow_factory_get_shadow (factory, priv->shadow_shape, shape_bounds.width, shape_bounds.height, shadow_class, appears_focused); } if (old_shadow != NULL) meta_shadow_unref (old_shadow); } void meta_window_actor_process_x11_damage (MetaWindowActor *self, XDamageNotifyEvent *event) { MetaWindowActorPrivate *priv = self->priv; if (priv->surface) meta_surface_actor_process_damage (priv->surface, event->area.x, event->area.y, event->area.width, event->area.height); } void meta_window_actor_sync_visibility (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; if (CLUTTER_ACTOR_IS_VISIBLE (self) != priv->visible) { if (priv->visible) clutter_actor_show (CLUTTER_ACTOR (self)); else clutter_actor_hide (CLUTTER_ACTOR (self)); } } 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 build_and_scan_frame_mask (MetaWindowActor *self, cairo_rectangle_int_t *client_area, cairo_region_t *shape_region) { ClutterBackend *backend = clutter_get_default_backend (); CoglContext *ctx = clutter_backend_get_cogl_context (backend); MetaWindowActorPrivate *priv = self->priv; guchar *mask_data; guint tex_width, tex_height; MetaShapedTexture *stex; CoglTexture *paint_tex, *mask_texture; int stride; cairo_t *cr; cairo_surface_t *surface; stex = meta_surface_actor_get_texture (priv->surface); g_return_if_fail (stex); meta_shaped_texture_set_mask_texture (stex, NULL); paint_tex = meta_shaped_texture_get_texture (stex); if (paint_tex == NULL) return; tex_width = cogl_texture_get_width (paint_tex); tex_height = cogl_texture_get_height (paint_tex); stride = cairo_format_stride_for_width (CAIRO_FORMAT_A8, tex_width); /* Create data for an empty image */ mask_data = g_malloc0 (stride * tex_height); surface = cairo_image_surface_create_for_data (mask_data, CAIRO_FORMAT_A8, tex_width, tex_height, stride); cr = cairo_create (surface); gdk_cairo_region (cr, shape_region); cairo_fill (cr); if (priv->window->frame != NULL) { cairo_region_t *frame_paint_region, *scanned_region; cairo_rectangle_int_t rect = { 0, 0, tex_width, tex_height }; /* 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 (priv->window->frame, cr); cairo_surface_flush (surface); 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 (surface); if (meta_texture_rectangle_check (paint_tex)) { mask_texture = COGL_TEXTURE (cogl_texture_rectangle_new_with_size (ctx, tex_width, tex_height)); cogl_texture_set_components (mask_texture, COGL_TEXTURE_COMPONENTS_A); cogl_texture_set_region (mask_texture, 0, 0, /* src_x/y */ 0, 0, /* dst_x/y */ tex_width, tex_height, /* dst_width/height */ tex_width, tex_height, /* width/height */ COGL_PIXEL_FORMAT_A_8, stride, mask_data); } else { CoglError *error = NULL; mask_texture = COGL_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); cogl_error_free (error); } } meta_shaped_texture_set_mask_texture (stex, mask_texture); if (mask_texture) cogl_object_unref (mask_texture); g_free (mask_data); } static void meta_window_actor_update_shape_region (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; cairo_region_t *region = NULL; cairo_rectangle_int_t client_area; meta_window_get_client_area_rect (priv->window, &client_area); if (priv->window->frame != NULL && priv->window->shape_region != NULL) { region = cairo_region_copy (priv->window->shape_region); cairo_region_translate (region, client_area.x, client_area.y); } else if (priv->window->shape_region != NULL) { region = cairo_region_reference (priv->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 ((priv->window->shape_region != NULL) || (priv->window->frame != NULL)) build_and_scan_frame_mask (self, &client_area, region); g_clear_pointer (&priv->shape_region, cairo_region_destroy); priv->shape_region = region; g_clear_pointer (&priv->shadow_shape, meta_window_shape_unref); meta_window_actor_invalidate_shadow (self); } static void meta_window_actor_update_input_region (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; MetaWindow *window = priv->window; 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 (priv->surface, region); cairo_region_destroy (region); } static void meta_window_actor_update_opaque_region (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; cairo_region_t *opaque_region; gboolean argb32 = is_argb32 (self); if (argb32 && priv->window->opaque_region != NULL) { cairo_rectangle_int_t client_area; meta_window_get_client_area_rect (priv->window, &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 ukwm's * case, graphical glitches will occur. */ opaque_region = cairo_region_copy (priv->window->opaque_region); cairo_region_translate (opaque_region, client_area.x, client_area.y); cairo_region_intersect (opaque_region, priv->shape_region); } else if (argb32) opaque_region = NULL; else opaque_region = cairo_region_reference (priv->shape_region); meta_surface_actor_set_opaque_region (priv->surface, opaque_region); cairo_region_destroy (opaque_region); } static void check_needs_reshape (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; if (!priv->needs_reshape) return; meta_window_actor_update_shape_region (self); if (priv->window->client_type == META_WINDOW_CLIENT_TYPE_X11) { meta_window_actor_update_input_region (self); meta_window_actor_update_opaque_region (self); } priv->needs_reshape = FALSE; } void meta_window_actor_update_shape (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; priv->needs_reshape = TRUE; if (is_frozen (self)) return; clutter_actor_queue_redraw (CLUTTER_ACTOR (priv->surface)); } static void meta_window_actor_handle_updates (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; if (is_frozen (self)) { /* The window is frozen due to a pending animation: we'll wait until * the animation finishes to reshape and repair the window */ return; } if (meta_surface_actor_is_unredirected (priv->surface)) return; meta_surface_actor_pre_paint (priv->surface); if (!meta_surface_actor_is_visible (priv->surface)) return; check_needs_reshape (self); check_needs_shadow (self); } void meta_window_actor_pre_paint (MetaWindowActor *self) { if (meta_window_actor_is_destroyed (self)) return; meta_window_actor_handle_updates (self); assign_frame_counter_to_frames (self); } static void do_send_frame_drawn (MetaWindowActor *self, FrameData *frame) { MetaWindowActorPrivate *priv = self->priv; MetaDisplay *display = meta_window_get_display (priv->window); Display *xdisplay = meta_display_get_xdisplay (display); XClientMessageEvent ev = { 0, }; frame->frame_drawn_time = meta_compositor_monotonic_time_to_server_time (display, g_get_monotonic_time ()); priv->frame_drawn_time = frame->frame_drawn_time; ev.type = ClientMessage; ev.window = meta_window_get_xwindow (priv->window); ev.message_type = 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_error_trap_push (display); XSendEvent (xdisplay, ev.window, False, 0, (XEvent*) &ev); XFlush (xdisplay); meta_error_trap_pop (display); } void meta_window_actor_post_paint (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; priv->repaint_scheduled = FALSE; if (meta_window_actor_is_destroyed (self)) 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 (priv->send_frame_messages_timer == 0 && priv->needs_frame_drawn) { GList *l; for (l = priv->frames; l; l = l->next) { FrameData *frame = l->data; if (frame->frame_drawn_time == 0) do_send_frame_drawn (self, frame); } priv->needs_frame_drawn = FALSE; } if (priv->first_frame_state == DRAWING_FIRST_FRAME) { priv->first_frame_state = EMITTED_FIRST_FRAME; g_signal_emit (self, signals[FIRST_FRAME], 0); } } static void do_send_frame_timings (MetaWindowActor *self, FrameData *frame, gint refresh_interval, gint64 presentation_time) { MetaWindowActorPrivate *priv = self->priv; MetaDisplay *display = meta_window_get_display (priv->window); Display *xdisplay = meta_display_get_xdisplay (display); XClientMessageEvent ev = { 0, }; ev.type = ClientMessage; ev.window = meta_window_get_xwindow (priv->window); ev.message_type = 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) { gint64 presentation_time_server = meta_compositor_monotonic_time_to_server_time (display, presentation_time); gint64 presentation_time_offset = presentation_time_server - frame->frame_drawn_time; if (presentation_time_offset == 0) presentation_time_offset = 1; if ((gint32)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_error_trap_push (display); XSendEvent (xdisplay, ev.window, False, 0, (XEvent*) &ev); XFlush (xdisplay); meta_error_trap_pop (display); } static void send_frame_timings (MetaWindowActor *self, FrameData *frame, ClutterFrameInfo *frame_info, gint64 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 (self, frame, refresh_interval, presentation_time); } void meta_window_actor_frame_complete (MetaWindowActor *self, ClutterFrameInfo *frame_info, gint64 presentation_time) { MetaWindowActorPrivate *priv = self->priv; GList *l; if (meta_window_actor_is_destroyed (self)) return; for (l = priv->frames; l;) { GList *l_next = l->next; FrameData *frame = l->data; gint64 frame_counter = frame_info->frame_counter; if (frame->frame_counter != -1 && frame->frame_counter <= frame_counter) { if (G_UNLIKELY (frame->frame_drawn_time == 0)) g_warning ("%s: Frame has assigned frame counter but no frame drawn time", priv->window->desc); if (G_UNLIKELY (frame->frame_counter < frame_counter)) g_warning ("%s: frame_complete callback never occurred for frame %" G_GINT64_FORMAT, priv->window->desc, frame->frame_counter); priv->frames = g_list_delete_link (priv->frames, l); send_frame_timings (self, frame, frame_info, presentation_time); frame_data_free (frame); } l = l_next; } } void meta_window_actor_invalidate_shadow (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; priv->recompute_focused_shadow = TRUE; priv->recompute_unfocused_shadow = TRUE; if (is_frozen (self)) return; clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } void meta_window_actor_update_opacity (MetaWindowActor *self) { MetaWindowActorPrivate *priv = self->priv; 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 = self->priv; 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 = self->priv; MetaWindow *window = priv->window; meta_window_actor_set_updates_frozen (self, meta_window_updates_are_frozen (window)); } ukwm/src/org.ukui.ukwm.ScreenCast.xml0000664000175000017500000000563113220600404016546 0ustar fengfeng ukwm/src/meta-enum-types.c.in0000664000175000017500000000163013220600404015044 0ustar fengfeng/*** 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 volatile gsize g_enum_type_id__volatile = 0; if (g_once_init_enter (&g_enum_type_id__volatile)) { 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 g_enum_type_id; g_enum_type_id = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); g_once_init_leave (&g_enum_type_id__volatile, g_enum_type_id); } return g_enum_type_id__volatile; } /*** END value-tail ***/ ukwm/src/x11/0000775000175000017500000000000013254604522011666 5ustar fengfengukwm/src/x11/ukwm-Xatomtype.h0000664000175000017500000001236613220600404015006 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * \file ukwm-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 Copyright 2017 Tianjin KYLIN Information Technology Co., 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. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 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_ */ ukwm/src/x11/group-props.c0000664000175000017500000001370613220600404014322 0ustar fengfeng/* -*- 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 #include "group-props.h" #include "group-private.h" #include "xprops.h" #include typedef void (* InitValueFunc) (MetaDisplay *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 (MetaDisplay *display, Atom property, MetaPropValue *value); static void reload_prop_value (MetaGroup *group, MetaPropValue *value); static MetaGroupPropHooks* find_hooks (MetaDisplay *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->display, properties[i], &values[i]); ++i; } meta_prop_get_values (group->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 (MetaDisplay *display, Atom property, MetaPropValue *value) { MetaGroupPropHooks *hooks; value->type = META_PROP_VALUE_INVALID; value->atom = None; hooks = find_hooks (display, property); if (hooks && hooks->init_func != NULL) (* hooks->init_func) (display, property, value); } static void reload_prop_value (MetaGroup *group, MetaPropValue *value) { MetaGroupPropHooks *hooks; hooks = find_hooks (group->display, value->atom); if (hooks && hooks->reload_func != NULL) (* hooks->reload_func) (group, value); } static void init_wm_client_machine (MetaDisplay *display, Atom property, MetaPropValue *value) { value->type = META_PROP_VALUE_STRING; value->atom = 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 (MetaDisplay *display, Atom property, MetaPropValue *value) { value->type = META_PROP_VALUE_UTF8; value->atom = 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_display_init_group_prop_hooks (MetaDisplay *display) { int i; MetaGroupPropHooks *hooks; g_assert (display->group_prop_hooks == NULL); display->group_prop_hooks = g_new0 (MetaGroupPropHooks, N_HOOKS); hooks = display->group_prop_hooks; i = 0; hooks[i].property = 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 = display->atom__NET_WM_PID; hooks[i].init_func = NULL; hooks[i].reload_func = NULL; ++i; hooks[i].property = 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_display_free_group_prop_hooks (MetaDisplay *display) { g_assert (display->group_prop_hooks != NULL); g_free (display->group_prop_hooks); display->group_prop_hooks = NULL; } static MetaGroupPropHooks* find_hooks (MetaDisplay *display, Atom property) { int i; /* FIXME we could sort the array and do binary search or * something */ i = 0; while (i < N_HOOKS) { if (display->group_prop_hooks[i].property == property) return &display->group_prop_hooks[i]; ++i; } return NULL; } ukwm/src/x11/events.c0000664000175000017500000016331313220600404013331 0ustar fengfeng/* -*- 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) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include "config.h" #include "x11/events.h" #include #include #include #include #include "meta/meta-backend.h" #include "bell.h" #include "display-private.h" #include "window-private.h" #include "workspace-private.h" #include "backends/x11/meta-backend-x11.h" #include "x11/window-x11.h" #include "x11/xprops.h" #ifdef HAVE_WAYLAND #include "wayland/meta-xwayland.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-xwayland-private.h" #endif static XIEvent * get_input_event (MetaDisplay *display, XEvent *event) { if (event->type == GenericEvent && event->xcookie.extension == 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; #ifdef HAVE_XI23 case XI_BarrierHit: case XI_BarrierLeave: if (((XIBarrierEvent *) input_event)->deviceid == META_VIRTUAL_CORE_POINTER_ID) return input_event; break; #endif /* HAVE_XI23 */ default: break; } } return NULL; } static Window xievent_get_modified_window (MetaDisplay *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; #ifdef HAVE_XI23 case XI_BarrierHit: case XI_BarrierLeave: return ((XIBarrierEvent *) input_event)->event; #endif /* HAVE_XI23 */ } 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 (MetaDisplay *display, XEvent *event) { XIEvent *input_event = get_input_event (display, event); if (input_event) return xievent_get_modified_window (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_DISPLAY_HAS_SHAPE (display) && event->type == (display->shape_event_base + ShapeNotify)) { XShapeEvent *sev = (XShapeEvent*) event; return sev->window; } return None; } } static guint32 event_get_time (MetaDisplay *display, XEvent *event) { XIEvent *input_event = get_input_event (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 CurrentTime; } } 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 (MetaDisplay *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; #ifdef HAVE_XI23 case XI_BarrierHit: name = "XI_BarrierHit"; break; case XI_BarrierLeave: name = "XI_BarrierLeave"; break; #endif /* HAVE_XI23 */ } 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 (MetaDisplay *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_error_trap_push (display); str = XGetAtomName (display->xdisplay, event->xproperty.atom); meta_error_trap_pop (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_error_trap_push (display); str = XGetAtomName (display->xdisplay, event->xclient.message_type); meta_error_trap_pop (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_DISPLAY_HAS_XSYNC (display) && event->type == (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_DISPLAY_HAS_SHAPE (display) && event->type == (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 (MetaDisplay *display, XEvent *event) { MetaScreen *screen = display->screen; const char *name = NULL; char *extra = NULL; char *winname; char *ret; XIEvent *input_event; input_event = get_input_event (display, event); if (input_event) meta_spew_xi2_event (display, input_event, &name, &extra); else meta_spew_core_event (display, event, &name, &extra); if (event->xany.window == screen->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 (MetaDisplay *display, XEvent *event) { char *event_str; /* filter overnumerous events */ if (event->type == Expose || event->type == MotionNotify || event->type == NoExpose) return; if (event->type == (display->damage_event_base + XDamageNotify)) return; if (event->type == (display->xsync_event_base + XSyncAlarmNotify)) return; if (event->type == PropertyNotify && event->xproperty.atom == display->atom__NET_WM_USER_TIME) return; event_str = meta_spew_event (display, event); g_print ("%s\n", event_str); g_free (event_str); } static gboolean handle_window_focus_event (MetaDisplay *display, MetaWindow *window, XIEnterEvent *event, unsigned long serial) { 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_display_xwindow_is_a_no_focus_window (display, event->event)) window_type = "no_focus_window"; else if (event->event == display->screen->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 ukwm'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) { display->server_focus_window = event->event; 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; } display->server_focus_window = None; 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 (display->server_focus_serial > display->focus_serial || (!display->focused_by_us && display->server_focus_serial == display->focus_serial)) { meta_display_update_focus_window (display, focus_window, focus_window ? focus_window->xwindow : None, display->server_focus_serial, FALSE); return TRUE; } else { return FALSE; } } static gboolean crossing_serial_is_ignored (MetaDisplay *display, unsigned long serial) { int i; i = 0; while (i < N_IGNORED_CROSSING_SERIALS) { if (display->ignored_crossing_serials[i] == serial) return TRUE; ++i; } return FALSE; } static gboolean handle_input_xevent (MetaDisplay *display, XIEvent *input_event, unsigned long serial) { XIEnterEvent *enter_event = (XIEnterEvent *) input_event; Window modified; MetaWindow *window; MetaScreen *screen = display->screen; 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 (display, input_event); window = modified != None ? meta_display_lookup_x_window (display, modified) : NULL; /* If this is an event for a GTK+ widget, let GTK+ handle it. */ if (meta_ui_window_is_widget (display->screen->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 (display, serial) && enter_event->mode != XINotifyGrab && enter_event->mode != XINotifyUngrab && enter_event->detail != XINotifyInferior && meta_display_focus_sentinel_clear (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 (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 (screen->active_workspace, NULL, meta_display_get_current_time_roundtrip (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 (screen->active_workspace, NULL, meta_display_get_current_time_roundtrip (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 (MetaDisplay *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 (display, xwindow, 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 (display->screen->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_error_trap_push (display); XChangeProperty (display->xdisplay, xwindow, display->atom__NET_FRAME_EXTENTS, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 4); meta_error_trap_pop (display); meta_XFree (hints); } /* from fvwm2, Copyright Matthias Clasen, Dominik Vogt */ static gboolean convert_property (MetaDisplay *display, MetaScreen *screen, Window w, Atom target, Atom property) { #define N_TARGETS 4 Atom conversion_targets[N_TARGETS]; long icccm_version[] = { 2, 0 }; conversion_targets[0] = display->atom_TARGETS; conversion_targets[1] = display->atom_MULTIPLE; conversion_targets[2] = display->atom_TIMESTAMP; conversion_targets[3] = display->atom_VERSION; meta_error_trap_push (display); if (target == display->atom_TARGETS) XChangeProperty (display->xdisplay, w, property, XA_ATOM, 32, PropModeReplace, (unsigned char *)conversion_targets, N_TARGETS); else if (target == display->atom_TIMESTAMP) XChangeProperty (display->xdisplay, w, property, XA_INTEGER, 32, PropModeReplace, (unsigned char *)&screen->wm_sn_timestamp, 1); else if (target == display->atom_VERSION) XChangeProperty (display->xdisplay, w, property, XA_INTEGER, 32, PropModeReplace, (unsigned char *)icccm_version, 2); else { meta_error_trap_pop_with_return (display); return FALSE; } if (meta_error_trap_pop_with_return (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 (display->xdisplay, False); return TRUE; } /* from fvwm2, Copyright Matthias Clasen, Dominik Vogt */ static void process_selection_request (MetaDisplay *display, XEvent *event) { MetaScreen *screen = display->screen; XSelectionEvent reply; if (screen->wm_sn_selection_window != event->xselectionrequest.owner || screen->wm_sn_atom != event->xselectionrequest.selection) { char *str; meta_error_trap_push (display); str = XGetAtomName (display->xdisplay, event->xselectionrequest.selection); meta_error_trap_pop (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 = 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 == display->atom_MULTIPLE) { if (event->xselectionrequest.property != None) { Atom type, *adata; int i, format; unsigned long num, rest; unsigned char *data; meta_error_trap_push (display); if (XGetWindowProperty (display->xdisplay, event->xselectionrequest.requestor, event->xselectionrequest.property, 0, 256, False, display->atom_ATOM_PAIR, &type, &format, &num, &rest, &data) != Success) { meta_error_trap_pop_with_return (display); return; } if (meta_error_trap_pop_with_return (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 (display, screen, event->xselectionrequest.requestor, adata[i], adata[i+1])) adata[i+1] = None; i += 2; } meta_error_trap_push (display); XChangeProperty (display->xdisplay, event->xselectionrequest.requestor, event->xselectionrequest.property, display->atom_ATOM_PAIR, 32, PropModeReplace, data, num); meta_error_trap_pop (display); meta_XFree (data); } } } else { if (event->xselectionrequest.property == None) event->xselectionrequest.property = event->xselectionrequest.target; if (convert_property (display, screen, event->xselectionrequest.requestor, event->xselectionrequest.target, event->xselectionrequest.property)) reply.property = event->xselectionrequest.property; } XSendEvent (display->xdisplay, event->xselectionrequest.requestor, False, 0L, (XEvent*)&reply); meta_verbose ("Handled selection request\n"); } static gboolean process_selection_clear (MetaDisplay *display, XEvent *event) { MetaScreen *screen = display->screen; if (screen->wm_sn_selection_window != event->xselectionclear.window || screen->wm_sn_atom != event->xselectionclear.selection) { char *str; meta_error_trap_push (display); str = XGetAtomName (display->xdisplay, event->xselectionclear.selection); meta_error_trap_pop (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", display->name); meta_display_unmanage_screen (display, display->screen, event->xselectionclear.time); return TRUE; } static void notify_bell (MetaDisplay *display, XkbAnyEvent *xkb_ev) { XkbBellNotifyEvent *xkb_bell_event = (XkbBellNotifyEvent*) xkb_ev; MetaWindow *window; window = meta_display_lookup_x_window (display, xkb_bell_event->window); if (!window && display->focus_window && display->focus_window->frame) window = display->focus_window; 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 (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 (MetaDisplay *display, XEvent *event) { Window modified; MetaWindow *window; MetaWindow *property_for_window; gboolean frame_was_receiver; gboolean bypass_gtk = FALSE; modified = event_get_modified_window (display, event); window = modified != None ? meta_display_lookup_x_window (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_DISPLAY_HAS_XSYNC (display) && event->type == (display->xsync_event_base + XSyncAlarmNotify)) { MetaWindow *alarm_window = meta_display_lookup_sync_alarm (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 (display->alarm_filter && display->alarm_filter (display, (XSyncAlarmNotifyEvent*)event, display->alarm_filter_data)) bypass_gtk = TRUE; } goto out; } if (META_DISPLAY_HAS_SHAPE (display) && event->type == (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 == display->screen->xroot) meta_stack_tracker_create_event (display->screen->stack_tracker, &event->xcreatewindow); } break; case DestroyNotify: { if (event->xdestroywindow.event == display->screen->xroot) meta_stack_tracker_destroy_event (display->screen->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_error_trap_push (display); meta_window_destroy_frame (window->frame->window); meta_error_trap_pop (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 == display->screen->xroot) { window = meta_window_x11_new (display, event->xmap.window, FALSE, META_COMP_EFFECT_CREATE); } 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 != window->screen->active_workspace) { meta_verbose ("Changing workspace due to MapRequest mapped = %d minimized = %d\n", window->mapped, window->minimized); meta_window_change_workspace (window, window->screen->active_workspace); } } break; case ReparentNotify: { if (event->xreparent.event == display->screen->xroot) meta_stack_tracker_reparent_event (display->screen->stack_tracker, &event->xreparent); } break; case ConfigureNotify: if (event->xconfigure.event != event->xconfigure.window) { if (event->xconfigure.event == display->screen->xroot && event->xconfigure.window != display->screen->composite_overlay_window) meta_stack_tracker_configure_event (display->screen->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_error_trap_push (display); XConfigureWindow (display->xdisplay, event->xconfigurerequest.window, xwcm, &xwc); meta_error_trap_pop (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_display_lookup_group (display, event->xproperty.window); if (group != NULL) meta_group_property_notify (group, event); if (event->xproperty.window == display->screen->xroot) { if (event->xproperty.atom == display->atom__NET_DESKTOP_LAYOUT) meta_screen_update_workspace_layout (display->screen); else if (event->xproperty.atom == display->atom__NET_DESKTOP_NAMES) meta_screen_update_workspace_names (display->screen); /* 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 == display->atom__UKWM_SENTINEL) { meta_display_decrement_focus_sentinel (display); } } } break; case SelectionRequest: process_selection_request (display, event); break; case SelectionNotify: break; case ColormapNotify: break; case ClientMessage: if (window) { #ifdef HAVE_WAYLAND if (event->xclient.message_type == display->atom_WL_SURFACE_ID) { guint32 surface_id = event->xclient.data.l[0]; meta_xwayland_handle_wl_surface_id (window, surface_id); } else #endif if (!frame_was_receiver) meta_window_x11_client_message (window, event); } else { if (event->xclient.window == display->screen->xroot) { if (event->xclient.message_type == 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_screen_get_workspace_by_index (display->screen, space); /* Handle clients using the older version of the spec... */ if (time == 0 && workspace) { meta_warning ("Received a NET_CURRENT_DESKTOP message " "from a broken (outdated) client who sent " "a 0 timestamp\n"); time = meta_display_get_current_time_roundtrip (display); } if (workspace) meta_workspace_activate (workspace, time); else meta_verbose ("Don't know about workspace %d\n", space); } else if (event->xclient.message_type == 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 == 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_display_get_current_time_roundtrip (display); meta_verbose ("Request to %s desktop\n", showing_desktop ? "show" : "hide"); if (showing_desktop) meta_screen_show_desktop (display->screen, timestamp); else { meta_screen_unshow_desktop (display->screen); meta_workspace_focus_default_window (display->screen->active_workspace, NULL, timestamp); } } else if (event->xclient.message_type == display->atom_WM_PROTOCOLS) { meta_verbose ("Received WM_PROTOCOLS message\n"); if ((Atom)event->xclient.data.l[0] == 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 == display->atom__NET_REQUEST_FRAME_EXTENTS) { meta_verbose ("Received _NET_REQUEST_FRAME_EXTENTS message\n"); process_request_frame_extents (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 (display->xdisplay)) { XEvent next_event; XPeekEvent (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 == display->xkb_base_event_type) { XkbAnyEvent *xkb_ev = (XkbAnyEvent *) event; switch (xkb_ev->xkb_type) { case XkbBellNotify: if (XSERVER_TIME_IS_BEFORE(display->last_bell_time, xkb_ev->time - 100)) { notify_bell (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; } /** * 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 Ukwm'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_display_handle_xevent (MetaDisplay *display, XEvent *event) { MetaBackend *backend = meta_get_backend (); Window modified; gboolean bypass_compositor = FALSE, bypass_gtk = FALSE; XIEvent *input_event; #if 0 meta_spew_event_print (display, event); #endif if (meta_startup_notification_handle_xevent (display->startup_notification, event)) { bypass_gtk = bypass_compositor = TRUE; goto out; } #ifdef HAVE_WAYLAND if (meta_is_wayland_compositor () && meta_xwayland_selection_handle_event (event)) { bypass_gtk = bypass_compositor = TRUE; goto out; } #endif display->current_time = event_get_time (display, event); if (META_IS_BACKEND_X11 (backend)) meta_backend_x11_handle_event (META_BACKEND_X11 (backend), event); if (display->focused_by_us && event->xany.serial > display->focus_serial && display->focus_window && !window_has_xwindow (display->focus_window, display->server_focus_window)) { meta_topic (META_DEBUG_FOCUS, "Earlier attempt to focus %s failed\n", display->focus_window->desc); meta_display_update_focus_window (display, meta_display_lookup_x_window (display, display->server_focus_window), display->server_focus_window, display->server_focus_serial, FALSE); } if (event->xany.window == display->screen->xroot) { if (meta_screen_handle_xevent (display->screen, event)) { bypass_gtk = bypass_compositor = TRUE; goto out; } } modified = event_get_modified_window (display, event); input_event = get_input_event (display, event); if (event->type == UnmapNotify) { if (meta_ui_window_should_not_cause_focus (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); } } #ifdef HAVE_XI23 if (meta_display_process_barrier_xevent (display, input_event)) { bypass_gtk = bypass_compositor = TRUE; goto out; } #endif /* HAVE_XI23 */ if (handle_input_xevent (display, input_event, event->xany.serial)) { bypass_gtk = bypass_compositor = TRUE; goto out; } if (handle_other_xevent (display, event)) { bypass_gtk = TRUE; goto out; } if (event->type == SelectionClear) { if (process_selection_clear (display, event)) { /* This means we called meta_display_unmanage_screen, which * means the MetaDisplay is effectively dead. We don't want * to poke into display->current_time below, since that would * crash, so just directly return. */ return TRUE; } } out: if (!bypass_compositor) { MetaWindow *window = modified != None ? meta_display_lookup_x_window (display, modified) : NULL; if (meta_compositor_process_event (display->compositor, event, window)) bypass_gtk = TRUE; } display->current_time = CurrentTime; return bypass_gtk; } static GdkFilterReturn xevent_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data) { MetaDisplay *display = data; if (meta_display_handle_xevent (display, xevent)) return GDK_FILTER_REMOVE; else return GDK_FILTER_CONTINUE; } void meta_display_init_events_x11 (MetaDisplay *display) { gdk_window_add_filter (NULL, xevent_filter, display); } void meta_display_free_events_x11 (MetaDisplay *display) { gdk_window_remove_filter (NULL, xevent_filter, display); } ukwm/src/x11/events.h0000664000175000017500000000210613220600404013326 0ustar fengfeng/* -*- 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 #ifndef META_EVENTS_X11_H #define META_EVENTS_X11_H void meta_display_init_events_x11 (MetaDisplay *display); void meta_display_free_events_x11 (MetaDisplay *display); #endif ukwm/src/x11/session.c0000664000175000017500000014350213220600404013506 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm Session Management */ /* * Copyright (C) 2001 Havoc Pennington (some code in here from * libgnomeui, (C) Tom Tromey, Carsten Schaar) * Copyright (C) 2004, 2005 Elijah Newren * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include #include "util-private.h" #include #include "session.h" #include #include #include #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 #include #include "display-private.h" #include 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_source_remove (input_id); } } 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, "NorthWestGravity") == 0) return NorthWestGravity; else if (strcmp (str, "NorthGravity") == 0) return NorthGravity; else if (strcmp (str, "NorthEastGravity") == 0) return NorthEastGravity; else if (strcmp (str, "WestGravity") == 0) return WestGravity; else if (strcmp (str, "CenterGravity") == 0) return CenterGravity; else if (strcmp (str, "EastGravity") == 0) return EastGravity; else if (strcmp (str, "SouthWestGravity") == 0) return SouthWestGravity; else if (strcmp (str, "SouthGravity") == 0) return SouthGravity; else if (strcmp (str, "SouthEastGravity") == 0) return SouthEastGravity; else if (strcmp (str, "StaticGravity") == 0) return StaticGravity; else return NorthWestGravity; } 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 *ukwm_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. */ ukwm_dir = g_strconcat (g_get_user_config_dir (), G_DIR_SEPARATOR_S "ukwm", NULL); session_dir = g_strconcat (ukwm_dir, G_DIR_SEPARATOR_S "sessions", NULL); if (mkdir (ukwm_dir, 0700) < 0 && errno != EEXIST) { meta_warning ("Could not create directory '%s': %s\n", ukwm_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; n = meta_workspace_index (window->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 (ukwm_dir); g_free (session_dir); } typedef enum { WINDOW_TAG_NONE, WINDOW_TAG_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 ukwm_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 "ukwm" 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 ~/.ukwm */ session_file = g_strconcat (g_get_home_dir (), G_DIR_SEPARATOR_S ".ukwm" 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 (&ukwm_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, "ukwm_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, "ukwm_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 ("UKWM_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 = NorthWestGravity; 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 "ukwm" 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()->screen->screen_name, NULL, 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 */ ukwm/src/x11/atomnames.h0000664000175000017500000001242213220600404014010 0ustar fengfeng/* -*- 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 * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ /* * \file atomnames.h A list of atom names. * * This is a list of the names of all the X atoms that Ukwm 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(_UKWM_HINTS) item(_GTK_THEME_VARIANT) item(_GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED) 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(_GNOME_WM_KEYBINDINGS) item(_GNOME_PANEL_ACTION) item(_GNOME_PANEL_ACTION_MAIN_MENU) item(_GNOME_PANEL_ACTION_RUN_DIALOG) item(_UKWM_TIMESTAMP_PING) item(_UKWM_FOCUS_SET) item(_UKWM_SENTINEL) item(_UKWM_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) /* 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) /* eof atomnames.h */ ukwm/src/x11/session.h0000664000175000017500000000465213220600404013515 0ustar fengfeng/* -*- 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 "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. */ int 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 ukwm/src/x11/group.c0000664000175000017500000002025213220600404013153 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2002 Red Hat Inc. * Copyright (C) 2003 Rob Adams * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ /** * SECTION:group * @title: MetaGroup * @short_description: Ukwm window groups * */ #include #include #include "group-private.h" #include "group-props.h" #include "window-private.h" #include #include static MetaGroup* meta_group_new (MetaDisplay *display, Window group_leader) { MetaGroup *group; #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->display = 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 (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 (display->groups_by_leader == NULL) display->groups_by_leader = g_hash_table_new (meta_unsigned_long_hash, meta_unsigned_long_equal); g_assert (g_hash_table_lookup (display->groups_by_leader, &group_leader) == NULL); g_hash_table_insert (display->groups_by_leader, &group->group_leader, group); /* Fill these in the order we want them to be gotten */ i = 0; initial_props[i++] = display->atom_WM_CLIENT_MACHINE; initial_props[i++] = display->atom__NET_WM_PID; initial_props[i++] = 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 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->display->groups_by_leader != NULL); g_hash_table_remove (group->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->display->groups_by_leader) == 0) { g_hash_table_destroy (group->display->groups_by_leader); group->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; /* 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 (window->display->groups_by_leader) { if (ancestor != window) group = ancestor->group; else if (window->xgroup_leader != None) group = g_hash_table_lookup (window->display->groups_by_leader, &window->xgroup_leader); else group = g_hash_table_lookup (window->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 (window->display, ancestor->xgroup_leader); else if (window->xgroup_leader != None) group = meta_group_new (window->display, window->xgroup_leader); else group = meta_group_new (window->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_display_lookup_group: (skip) * @display: a #MetaDisplay * @group_leader: a X window * */ MetaGroup* meta_display_lookup_group (MetaDisplay *display, Window group_leader) { MetaGroup *group; group = NULL; if (display->groups_by_leader) group = g_hash_table_lookup (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->screen->stack); frozen_stacks = g_slist_prepend (frozen_stacks, window->screen->stack); meta_stack_update_layer (window->screen->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; } ukwm/src/x11/window-props.c0000664000175000017500000020261413220600404014473 0ustar fengfeng/* -*- 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 * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #define _XOPEN_SOURCE 500 /* for gethostname() */ #include #include "window-props.h" #include "window-x11.h" #include "window-x11-private.h" #include #include "xprops.h" #include "frame.h" #include #include #include #include #include "util-private.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 (MetaDisplay *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, property); if (!hooks) return; if ((hooks->flags & INIT_ONLY) && !initial) return; init_prop_value (window, hooks, &value); meta_prop_get_values (window->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; values = g_new0 (MetaPropValue, window->display->n_prop_hooks); j = 0; for (i = 0; i < window->display->n_prop_hooks; i++) { MetaWindowPropHooks *hooks = &window->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, window->xwindow, values, n_properties); j = 0; for (i = 0; i < window->display->n_prop_hooks; i++) { MetaWindowPropHooks *hooks = &window->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) { 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 == window->display->atom__NET_WM_WINDOW_TYPE_DESKTOP || atom == window->display->atom__NET_WM_WINDOW_TYPE_DOCK || atom == window->display->atom__NET_WM_WINDOW_TYPE_TOOLBAR || atom == window->display->atom__NET_WM_WINDOW_TYPE_MENU || atom == window->display->atom__NET_WM_WINDOW_TYPE_UTILITY || atom == window->display->atom__NET_WM_WINDOW_TYPE_SPLASH || atom == window->display->atom__NET_WM_WINDOW_TYPE_DIALOG || atom == window->display->atom__NET_WM_WINDOW_TYPE_DROPDOWN_MENU || atom == window->display->atom__NET_WM_WINDOW_TYPE_POPUP_MENU || atom == window->display->atom__NET_WM_WINDOW_TYPE_TOOLTIP || atom == window->display->atom__NET_WM_WINDOW_TYPE_NOTIFICATION || atom == window->display->atom__NET_WM_WINDOW_TYPE_COMBO || atom == window->display->atom__NET_WM_WINDOW_TYPE_DND || atom == window->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, 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->atom__NET_WM_ICON); } static void reload_kwm_win_icon (MetaWindow *window, MetaPropValue *value, gboolean initial) { reload_icon (window, window->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_display_unregister_x_window (window->display, window->user_time_window); /* Don't get events on not-managed windows */ XSelectInput (window->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_display_lookup_x_window (window->display, value->v.xwindow); if (prev_owner && prev_owner->user_time_window == value->v.xwindow) { meta_display_unregister_x_window (window->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_display_register_x_window (window->display, &window->user_time_window, window); /* Just listen for property notify events */ XSelectInput (window->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->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, window->xwindow, atom, *target); /* Bug 330671 -- Don't forget to clear _NET_WM_VISIBLE_(ICON_)NAME */ if (!modified && previous_was_modified) { meta_error_trap_push (window->display); XDeleteProperty (window->display->xdisplay, window->xwindow, atom); meta_error_trap_pop (window->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->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) { g_autofree gchar *title = g_convert (value->v.str, -1, "UTF-8", "LATIN1", NULL, NULL, NULL); set_window_title (window, title); 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_ukwm_hints (MetaWindow *window, MetaPropValue *value, gboolean initial) { if (value->type != META_PROP_VALUE_INVALID) { char *new_hints = value->v.str; char *old_hints = window->ukwm_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->ukwm_hints = g_strdup (new_hints); else window->ukwm_hints = NULL; g_object_notify (G_OBJECT (window), "ukwm-hints"); } } else if (window->ukwm_hints) { g_free (window->ukwm_hints); window->ukwm_hints = NULL; g_object_notify (G_OBJECT (window), "ukwm-hints"); } } static void reload_net_wm_state (MetaWindow *window, MetaPropValue *value, gboolean initial) { 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] == window->display->atom__NET_WM_STATE_SHADED) window->shaded = TRUE; else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_MAXIMIZED_HORZ) window->maximize_horizontally_after_placement = TRUE; else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_MAXIMIZED_VERT) window->maximize_vertically_after_placement = TRUE; else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_HIDDEN) window->minimize_after_placement = TRUE; else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_MODAL) priv->wm_state_modal = TRUE; else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_SKIP_TASKBAR) priv->wm_state_skip_taskbar = TRUE; else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_SKIP_PAGER) priv->wm_state_skip_pager = TRUE; else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_FULLSCREEN) { window->fullscreen = TRUE; g_object_notify (G_OBJECT (window), "fullscreen"); } else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_ABOVE) window->wm_state_above = TRUE; else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_BELOW) window->wm_state_below = TRUE; else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_STATE_DEMANDS_ATTENTION) window->wm_state_demands_attention = TRUE; else if (value->v.atom_list.atoms[i] == window->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) { 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_screen_apply_startup_properties (window->screen, window)) { if (window->initial_timestamp_set) timestamp = window->initial_timestamp; if (window->initial_workspace_set) workspace = meta_screen_get_workspace_by_index (window->screen, 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_TOGGLED_ON(old,new,flag) \ (((old)->flags & (flag)) == 0 && \ ((new)->flags & (flag)) != 0) #define FLAG_TOGGLED_OFF(old,new,flag) \ (((old)->flags & (flag)) != 0 && \ ((new)->flags & (flag)) == 0) #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); } 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 = NorthWestGravity; 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; 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); 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; window->take_focus = FALSE; window->delete_window = FALSE; window->can_ping = 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->atom_WM_TAKE_FOCUS) window->take_focus = TRUE; else if (value->v.atom_list.atoms[i] == window->display->atom_WM_DELETE_WINDOW) window->delete_window = TRUE; else if (value->v.atom_list.atoms[i] == window->display->atom__NET_WM_PING) window->can_ping = 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, XA_WM_HINTS); meta_window_queue (window, META_QUEUE_UPDATE_ICON | META_QUEUE_MOVE_RESIZE); } 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_display_lookup_x_window (window->display, transient_for); if (!parent) { meta_warning ("Invalid WM_TRANSIENT_FOR window 0x%lx specified for %s.\n", transient_for, window->desc); transient_for = None; } /* Make sure there is not a loop */ while (parent) { if (parent == window) { meta_warning ("WM_TRANSIENT_FOR window 0x%lx for %s would create loop.\n", transient_for, window->desc); transient_for = None; break; } parent = meta_display_lookup_x_window (parent->display, parent->xtransient_for); } } 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->screen->xroot) meta_window_set_transient_for (window, NULL); else { parent = meta_display_lookup_x_window (window->display, window->xtransient_for); 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_gtk_hide_titlebar_when_maximized (MetaWindow *window, MetaPropValue *value, gboolean initial) { gboolean requested_value = FALSE; gboolean current_value = window->hide_titlebar_when_maximized; if (!meta_prefs_get_ignore_request_hide_titlebar () && value->type != META_PROP_VALUE_INVALID) { requested_value = ((int) value->v.cardinal == 1); meta_verbose ("Request to hide titlebar for window %s.\n", window->desc); } if (requested_value == current_value) return; window->hide_titlebar_when_maximized = requested_value; if (META_WINDOW_MAXIMIZED (window)) { meta_window_queue (window, META_QUEUE_MOVE_RESIZE); meta_window_frame_size_changed (window); 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); } #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_display_init_window_prop_hooks: * @display: The #MetaDisplay * * 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_display_init_window_prop_hooks (MetaDisplay *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[] = { { display->atom_WM_CLIENT_MACHINE, META_PROP_VALUE_STRING, reload_wm_client_machine, LOAD_INIT | INCLUDE_OR }, { 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 }, { 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 }, { display->atom__UKWM_HINTS, META_PROP_VALUE_TEXT_PROPERTY, reload_ukwm_hints, LOAD_INIT | INCLUDE_OR }, { display->atom__NET_WM_OPAQUE_REGION, META_PROP_VALUE_CARDINAL_LIST, reload_opaque_region, LOAD_INIT | INCLUDE_OR }, { display->atom__NET_WM_DESKTOP, META_PROP_VALUE_CARDINAL, reload_net_wm_desktop, LOAD_INIT | INIT_ONLY }, { display->atom__NET_STARTUP_ID, META_PROP_VALUE_UTF8, reload_net_startup_id, LOAD_INIT }, { 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 }, { 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 }, { display->atom__NET_WM_USER_TIME, META_PROP_VALUE_CARDINAL, reload_net_wm_user_time, LOAD_INIT }, { display->atom__NET_WM_STATE, META_PROP_VALUE_ATOM_LIST, reload_net_wm_state, LOAD_INIT | INIT_ONLY }, { 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 }, { display->atom__GTK_THEME_VARIANT, META_PROP_VALUE_UTF8, reload_gtk_theme_variant, LOAD_INIT }, { display->atom__GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED, META_PROP_VALUE_CARDINAL, reload_gtk_hide_titlebar_when_maximized, LOAD_INIT }, { display->atom__GTK_APPLICATION_ID, META_PROP_VALUE_UTF8, reload_gtk_application_id, LOAD_INIT }, { display->atom__GTK_UNIQUE_BUS_NAME, META_PROP_VALUE_UTF8, reload_gtk_unique_bus_name, LOAD_INIT }, { display->atom__GTK_APPLICATION_OBJECT_PATH, META_PROP_VALUE_UTF8, reload_gtk_application_object_path, LOAD_INIT }, { display->atom__GTK_WINDOW_OBJECT_PATH, META_PROP_VALUE_UTF8, reload_gtk_window_object_path, LOAD_INIT }, { display->atom__GTK_APP_MENU_OBJECT_PATH, META_PROP_VALUE_UTF8, reload_gtk_app_menu_object_path, LOAD_INIT }, { display->atom__GTK_MENUBAR_OBJECT_PATH, META_PROP_VALUE_UTF8, reload_gtk_menubar_object_path, LOAD_INIT }, { display->atom__GTK_FRAME_EXTENTS, META_PROP_VALUE_CARDINAL_LIST,reload_gtk_frame_extents, LOAD_INIT }, { display->atom__NET_WM_USER_TIME_WINDOW, META_PROP_VALUE_WINDOW, reload_net_wm_user_time_window, LOAD_INIT }, { display->atom__NET_WM_ICON, META_PROP_VALUE_INVALID, reload_net_wm_icon, NONE }, { display->atom__KWM_WIN_ICON, META_PROP_VALUE_INVALID, reload_kwm_win_icon, NONE }, { display->atom__NET_WM_ICON_GEOMETRY, META_PROP_VALUE_CARDINAL_LIST, reload_icon_geometry, LOAD_INIT }, { display->atom_WM_CLIENT_LEADER, META_PROP_VALUE_INVALID, complain_about_broken_client, NONE }, { display->atom_SM_CLIENT_ID, META_PROP_VALUE_INVALID, complain_about_broken_client, NONE }, { display->atom_WM_WINDOW_ROLE, META_PROP_VALUE_STRING, reload_wm_window_role, LOAD_INIT | FORCE_INIT }, { display->atom__NET_WM_WINDOW_TYPE, META_PROP_VALUE_ATOM_LIST, reload_net_wm_window_type, LOAD_INIT | INCLUDE_OR | FORCE_INIT }, { display->atom__NET_WM_STRUT, META_PROP_VALUE_INVALID, reload_struts, NONE }, { display->atom__NET_WM_STRUT_PARTIAL, META_PROP_VALUE_INVALID, reload_struts, NONE }, { display->atom__NET_WM_BYPASS_COMPOSITOR, META_PROP_VALUE_CARDINAL, reload_bypass_compositor, LOAD_INIT | INCLUDE_OR }, { display->atom__NET_WM_WINDOW_OPACITY, META_PROP_VALUE_CARDINAL, reload_window_opacity, LOAD_INIT | INCLUDE_OR }, { 0 }, }; MetaWindowPropHooks *table = g_memdup (hooks, sizeof (hooks)), *cursor = table; g_assert (display->prop_hooks == NULL); display->prop_hooks_table = (gpointer) table; 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 (display->prop_hooks, GINT_TO_POINTER (cursor->property), cursor); cursor++; } display->n_prop_hooks = cursor - table; } void meta_display_free_window_prop_hooks (MetaDisplay *display) { g_hash_table_unref (display->prop_hooks); display->prop_hooks = NULL; g_free (display->prop_hooks_table); display->prop_hooks_table = NULL; } static MetaWindowPropHooks* find_hooks (MetaDisplay *display, Atom property) { return g_hash_table_lookup (display->prop_hooks, GINT_TO_POINTER (property)); } ukwm/src/x11/window-props.h0000664000175000017500000000633513220600404014502 0ustar fengfeng/* -*- 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 "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_display_init_window_prop_hooks: * @display: The display. * * Initialises the hooks used for the reload_propert* functions * on a particular display, and stores a pointer to them in the * display. */ void meta_display_init_window_prop_hooks (MetaDisplay *display); /** * meta_display_free_window_prop_hooks: * @display: The display. * Frees the hooks used for the reload_propert* functions * for a particular display. */ void meta_display_free_window_prop_hooks (MetaDisplay *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 */ ukwm/src/x11/iconcache.c0000664000175000017500000003503313220600404013736 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm window icons */ /* * Copyright (C) 2002 Havoc Pennington * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include "config.h" #include "iconcache.h" #include #include #include #include #include #include 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 (MetaDisplay *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_error_trap_push (display); type = None; data = NULL; result = XGetWindowProperty (display->xdisplay, xwindow, display->atom__NET_WM_ICON, 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems, &bytes_after, &data); err = meta_error_trap_pop_with_return (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 (MetaDisplay *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 (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 int standard_pict_format_for_depth (int depth) { switch (depth) { case 1: return PictStandardA1; case 24: return PictStandardRGB24; case 32: return PictStandardARGB32; default: g_assert_not_reached (); } } static XRenderPictFormat * pict_format_for_depth (Display *xdisplay, int depth) { return XRenderFindStandardFormat (xdisplay, standard_pict_format_for_depth (depth)); } static cairo_surface_t * surface_from_pixmap (Display *xdisplay, Pixmap xpixmap, int width, int height) { Window root_return; 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; return cairo_xlib_surface_create_with_xrender_format (xdisplay, xpixmap, DefaultScreenOfDisplay (xdisplay), pict_format_for_depth (xdisplay, depth_ret), w_ret, h_ret); } static gboolean try_pixmap_and_mask (MetaDisplay *display, Pixmap src_pixmap, Pixmap src_mask, cairo_surface_t **iconp) { Display *xdisplay = display->xdisplay; cairo_surface_t *icon, *mask = NULL; int w, h, d; if (src_pixmap == None) return FALSE; meta_error_trap_push (display); get_pixmap_geometry (display, src_pixmap, &w, &h, &d); icon = surface_from_pixmap (xdisplay, src_pixmap, w, h); if (icon && src_mask != None) { get_pixmap_geometry (display, src_mask, &w, &h, &d); if (d == 1) mask = surface_from_pixmap (xdisplay, src_mask, w, h); } meta_error_trap_pop (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 (MetaDisplay *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_error_trap_push (display); icons = NULL; result = XGetWindowProperty (display->xdisplay, xwindow, display->atom__KWM_WIN_ICON, 0, G_MAXLONG, False, display->atom__KWM_WIN_ICON, &type, &format, &nitems, &bytes_after, &data); icons = (Pixmap *)data; err = meta_error_trap_pop_with_return (display); if (err != Success || result != Success) return; if (type != 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, MetaDisplay *display, Atom atom) { if (atom == display->atom__NET_WM_ICON) icon_cache->net_wm_icon_dirty = TRUE; else if (atom == 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 (MetaScreen *screen, 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 (screen->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 (screen->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 (screen->display, xwindow, &pixmap, &mask); if ((pixmap != icon_cache->prev_pixmap || mask != icon_cache->prev_mask) && pixmap != None) { if (try_pixmap_and_mask (screen->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; } ukwm/src/x11/xprops.c0000664000175000017500000010016413220600404013353 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm X property convenience routines */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002 Red Hat Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., Ltd. * * 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 #include #include "xprops.h" #include #include "util-private.h" #include "ui.h" #include "ukwm-Xatomtype.h" #include "window-private.h" #include #include typedef struct { MetaDisplay *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) { 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_error_trap_push (results->display); type_name = XGetAtomName (results->display->xdisplay, results->type); expected_name = XGetAtomName (results->display->xdisplay, expected_type); prop_name = XGetAtomName (results->display->xdisplay, results->xatom); meta_error_trap_pop (results->display); w = meta_display_lookup_x_window (results->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); if (type_name) XFree (type_name); if (expected_name) XFree (expected_name); if (prop_name) 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 (MetaDisplay *display, Window xwindow, Atom xatom, Atom req_type, GetPropertyResults *results) { xcb_get_property_cookie_t cookie; xcb_connection_t *xcb_conn = XGetXCBConnection (display->xdisplay); results->display = 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 (MetaDisplay *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 (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 = calloc (1, sizeof (MotifWmHints)); if (*hints_p == NULL) { g_free (results->prop); results->prop = NULL; return FALSE; } 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 (MetaDisplay *display, Window xwindow, Atom xatom, MotifWmHints **hints_p) { GetPropertyResults results; *hints_p = NULL; if (!get_property (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 (MetaDisplay *display, Window xwindow, Atom xatom, char **str_p) { GetPropertyResults results; *str_p = NULL; if (!get_property (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->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->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->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_error_trap_push (results->display); name = XGetAtomName (results->display->xdisplay, results->xatom); meta_error_trap_pop (results->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 (MetaDisplay *display, Window xwindow, Atom xatom, char ***str_p, int *n_str_p) { GetPropertyResults results; *str_p = NULL; if (!get_property (display, xwindow, xatom, 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 (MetaDisplay *display, Window xwindow, Atom atom, const char *val) { meta_error_trap_push (display); XChangeProperty (display->xdisplay, xwindow, atom, display->atom_UTF8_STRING, 8, PropModeReplace, (guchar*) val, strlen (val)); meta_error_trap_pop (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 (MetaDisplay *display, Window xwindow, Atom xatom, Window *window_p) { GetPropertyResults results; *window_p = None; if (!get_property (display, xwindow, xatom, XA_WINDOW, &results)) return FALSE; return window_from_results (&results, window_p); } gboolean meta_prop_get_cardinal (MetaDisplay *display, Window xwindow, Atom xatom, uint32_t *cardinal_p) { return meta_prop_get_cardinal_with_atom_type (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 (MetaDisplay *display, Window xwindow, Atom xatom, Atom prop_type, uint32_t *cardinal_p) { GetPropertyResults results; *cardinal_p = 0; if (!get_property (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; 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; ret = g_strdup (local_list[0]); 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; results->prop = NULL; tp.encoding = results->type; tp.format = results->format; tp.nitems = results->n_items; *utf8_str_p = text_property_to_utf8 (results->display->xdisplay, &tp); if (tp.value != NULL) XFree (tp.value); 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 = calloc (1, sizeof (XWMHints)); 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, len_class; class_hint->res_class = NULL; class_hint->res_name = NULL; if (!validate_or_free_results (results, 8, XA_STRING, FALSE)) return FALSE; len_name = strlen ((char *) results->prop); if (! (class_hint->res_name = malloc (len_name+1))) { g_free (results->prop); results->prop = NULL; return FALSE; } strcpy (class_hint->res_name, (char *)results->prop); if (len_name == (int) results->n_items) len_name--; len_class = strlen ((char *)results->prop + len_name + 1); if (! (class_hint->res_class = malloc(len_class+1))) { XFree(class_hint->res_name); class_hint->res_name = NULL; g_free (results->prop); results->prop = NULL; return FALSE; } strcpy (class_hint->res_class, (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 = malloc (sizeof (XSizeHints)); 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 (MetaDisplay *display, Window xwindow, MetaPropValue *values, int n_values) { int i; xcb_get_property_cookie_t *tasks; xcb_connection_t *xcb_conn = XGetXCBConnection (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 = 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 (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.display = 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); 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: free (value->v.str); break; case META_PROP_VALUE_STRING_AS_UTF8: g_free (value->v.str); break; case META_PROP_VALUE_MOTIF_HINTS: free (value->v.motif_hints); break; case META_PROP_VALUE_CARDINAL: break; case META_PROP_VALUE_WINDOW: break; case META_PROP_VALUE_ATOM_LIST: free (value->v.atom_list.atoms); break; case META_PROP_VALUE_TEXT_PROPERTY: free (value->v.str); break; case META_PROP_VALUE_WM_HINTS: free (value->v.wm_hints); break; case META_PROP_VALUE_CLASS_HINT: free (value->v.class_hint.res_class); free (value->v.class_hint.res_name); break; case META_PROP_VALUE_SIZE_HINTS: 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: free (value->v.cardinal_list.cardinals); break; case META_PROP_VALUE_SYNC_COUNTER: break; case META_PROP_VALUE_SYNC_COUNTER_LIST: 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); } ukwm/src/x11/window-x11.h0000664000175000017500000000661213220600404013746 0ustar fengfeng/* -*- 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 #include 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; 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_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); #endif ukwm/src/x11/window-x11-private.h0000664000175000017500000000357413220600404015422 0ustar fengfeng/* -*- 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 "window-private.h" #include "x11/iconcache.h" G_BEGIN_DECLS typedef struct _MetaWindowX11Private MetaWindowX11Private; 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; /* 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; }; G_END_DECLS #endif ukwm/src/x11/window-x11.c0000664000175000017500000034175713220600404013755 0ustar fengfeng/* -*- 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 * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include "config.h" #include "window-x11.h" #include "window-x11-private.h" #include #include #include /* For display->resource_mask */ #include #include #include #include #include "core.h" #include #include #include #include #include "frame.h" #include "boxes-private.h" #include "window-private.h" #include "window-props.h" #include "xprops.h" #include "session.h" #include "workspace-private.h" #include "backends/meta-logical-monitor.h" #include "backends/x11/meta-backend-x11.h" struct _MetaWindowX11Class { MetaWindowClass parent_class; }; G_DEFINE_TYPE_WITH_PRIVATE (MetaWindowX11, meta_window_x11, META_TYPE_WINDOW) 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; ev.type = ClientMessage; ev.window = window->xwindow; ev.message_type = window->display->atom_WM_PROTOCOLS; ev.format = 32; ev.data.l[0] = atom; ev.data.l[1] = timestamp; meta_error_trap_push (window->display); XSendEvent (window->display->xdisplay, window->xwindow, False, 0, (XEvent*) &ev); meta_error_trap_pop (window->display); } static Window read_client_leader (MetaDisplay *display, Window xwindow) { Window retval = None; meta_prop_get_window (display, xwindow, 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) { char *str; window->xclient_leader = leader; if (meta_prop_get_latin1_string (window->display, leader, window->display->atom_SM_CLIENT_ID, &str)) { window->sm_client_id = g_strdup (str); meta_XFree (str); } } 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 */ char *str; str = NULL; if (meta_prop_get_latin1_string (window->display, window->xwindow, window->display->atom_SM_CLIENT_ID, &str)) { if (window->sm_client_id == NULL) /* first time through */ 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); window->sm_client_id = g_strdup (str); meta_XFree (str); } } } 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) { 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 = window->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_error_trap_push (window->display); XSendEvent (window->display->xdisplay, window->xwindow, False, StructureNotifyMask, &event); meta_error_trap_pop (window->display); } static void adjust_for_gravity (MetaWindow *window, gboolean coords_assume_border, int 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 (StaticGravity 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 == StaticGravity) 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, NorthEastGravity * 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 NorthGravity: case CenterGravity: case SouthGravity: ref_x += rect->width / 2 + bw; break; case NorthEastGravity: case EastGravity: case SouthEastGravity: ref_x += rect->width + bw * 2; break; default: break; } switch (gravity) { case WestGravity: case CenterGravity: case EastGravity: ref_y += rect->height / 2 + bw; break; case SouthWestGravity: case SouthGravity: case SouthEastGravity: 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 NorthGravity: case CenterGravity: case SouthGravity: rect->x -= frame_width / 2; break; case NorthEastGravity: case EastGravity: case SouthEastGravity: rect->x -= frame_width; break; } switch (gravity) { case WestGravity: case CenterGravity: case EastGravity: rect->y -= frame_height / 2; break; case SouthWestGravity: case SouthGravity: case SouthEastGravity: rect->y -= frame_height; 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) { MetaWorkspace *space; space = meta_screen_get_workspace_by_index (window->screen, 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); 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; int 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_display_register_x_window (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; int 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) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); meta_error_trap_push (window->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 (window->display->xdisplay, window->xwindow, window->display->atom__NET_WM_DESKTOP); XDeleteProperty (window->display->xdisplay, window->xwindow, window->display->atom__NET_WM_STATE); XDeleteProperty (window->display->xdisplay, window->xwindow, window->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) ukwm 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 (window->display->xdisplay, window->xwindow); } meta_display_unregister_x_window (window->display, window->xwindow); /* Put back anything we messed up */ if (priv->border_width != 0) XSetWindowBorderWidth (window->display->xdisplay, window->xwindow, priv->border_width); /* No save set */ XRemoveFromSaveSet (window->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_display_unregister_x_window (window->display, window->user_time_window); window->user_time_window = None; } if (META_DISPLAY_HAS_SHAPE (window->display)) XShapeSelectInput (window->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_error_trap_pop (window->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); } } static void meta_window_x11_ping (MetaWindow *window, guint32 serial) { MetaDisplay *display = window->display; send_icccm_message (window, display->atom__NET_WM_PING, serial); } static void meta_window_x11_delete (MetaWindow *window, guint32 timestamp) { meta_error_trap_push (window->display); if (window->delete_window) { meta_topic (META_DEBUG_WINDOW_OPS, "Deleting %s with delete_window request\n", window->desc); send_icccm_message (window, window->display->atom_WM_DELETE_WINDOW, timestamp); } else { meta_topic (META_DEBUG_WINDOW_OPS, "Deleting %s with explicit kill\n", window->desc); XKillClient (window->display->xdisplay, window->xwindow); } meta_error_trap_pop (window->display); } static void meta_window_x11_kill (MetaWindow *window) { meta_topic (META_DEBUG_WINDOW_OPS, "Disconnecting %s with XKillClient()\n", window->desc); meta_error_trap_push (window->display); XKillClient (window->display->xdisplay, window->xwindow); meta_error_trap_pop (window->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->atom_WM_TAKE_FOCUS, timestamp); } static void meta_window_x11_focus (MetaWindow *window, guint32 timestamp) { /* 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 || !(window->input || window->take_focus))) { meta_topic (META_DEBUG_FOCUS, "Focusing frame of %s\n", window->desc); meta_display_set_input_focus_window (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 (window->display, window, FALSE, timestamp); } if (window->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 now (and set * display->focus_window to that) before sending WM_TAKE_FOCUS. */ if (window->display->focus_window != NULL && window->display->focus_window->unmanaging) meta_display_focus_the_no_focus_window (window->display, window->screen, 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) { 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_error_trap_push (window->display); XChangeProperty (window->display->xdisplay, window->xwindow, window->display->atom__NET_FRAME_EXTENTS, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 4); meta_error_trap_pop (window->display); } static void update_gtk_edge_constraints (MetaWindow *window) { MetaEdgeConstraint *constraints = window->edge_constraints; unsigned long data[1]; /* Edge constraints */ data[0] = (constraints[0] != META_EDGE_CONSTRAINT_NONE ? 1 : 0) << 0 | (constraints[0] != META_EDGE_CONSTRAINT_MONITOR ? 1 : 0) << 1 | (constraints[1] != META_EDGE_CONSTRAINT_NONE ? 1 : 0) << 2 | (constraints[1] != META_EDGE_CONSTRAINT_MONITOR ? 1 : 0) << 3 | (constraints[2] != META_EDGE_CONSTRAINT_NONE ? 1 : 0) << 4 | (constraints[2] != META_EDGE_CONSTRAINT_MONITOR ? 1 : 0) << 5 | (constraints[3] != META_EDGE_CONSTRAINT_NONE ? 1 : 0) << 6 | (constraints[3] != META_EDGE_CONSTRAINT_MONITOR ? 1 : 0) << 7; meta_verbose ("Setting _GTK_EDGE_CONSTRAINTS to %lu\n", data[0]); meta_error_trap_push (window->display); XChangeProperty (window->display->xdisplay, window->xwindow, window->display->atom__GTK_EDGE_CONSTRAINTS, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 1); meta_error_trap_pop (window->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) { 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 = window->display->atom_WM_PROTOCOLS; ev.format = 32; ev.data.l[0] = window->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 (window->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, "[ukwm] 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) { /* 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_error_trap_push (window->display); XChangeProperty (window->display->xdisplay, window->xwindow, window->display->atom__NET_WM_DESKTOP, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 1); meta_error_trap_pop (window->display); } static void meta_window_x11_move_resize_internal (MetaWindow *window, int gravity, MetaRectangle unconstrained_rect, MetaRectangle constrained_rect, 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; /* We must send configure notify if we move but don't resize, since * the client window may not get a real event */ if ((need_move_client || need_move_frame) && !(need_resize_client || need_resize_frame)) need_configure_notify = TRUE; /* MapRequest events with a PPosition or UPosition hint with a frame * are moved by ukwm 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, ukwm 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; /* 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 StaticGravity * does nothing if you _only_ resize or _only_ move the frame; * it must move _and_ resize, otherwise you get NorthWestGravity * 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_error_trap_push (window->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->xdisplay, window->xwindow, mask, &values); meta_error_trap_pop (window->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; 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, window->xwindow, window->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_new (MetaStrut, 1); temp->side = 1 << i; /* See MetaSide def. Matches nicely, eh? */ temp->rect = window->screen->rect; switch (temp->side) { case META_SIDE_RIGHT: temp->rect.x = BOX_RIGHT(temp->rect) - thickness; /* Intentionally fall through without breaking */ 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; /* Intentionally fall through without breaking */ 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); } meta_XFree (struts); } else { meta_verbose ("No _NET_WM_STRUT property for %s\n", window->desc); } if (!new_struts && meta_prop_get_cardinal_list (window->display, window->xwindow, window->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_new (MetaStrut, 1); temp->side = 1 << i; temp->rect = window->screen->rect; switch (temp->side) { case META_SIDE_RIGHT: temp->rect.x = BOX_RIGHT(temp->rect) - thickness; /* Intentionally fall through without breaking */ case META_SIDE_LEFT: temp->rect.width = thickness; break; case META_SIDE_BOTTOM: temp->rect.y = BOX_BOTTOM(temp->rect) - thickness; /* Intentionally fall through without breaking */ 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); } meta_XFree (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 */ meta_free_gslist_and_elements (old_struts); 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->screen, 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, gboolean user_op) { 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) { xcb_connection_t *xcb = XGetXCBConnection (window->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; } 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; } void meta_window_x11_set_net_wm_state (MetaWindow *window) { 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] = window->display->atom__NET_WM_STATE_SHADED; ++i; } if (priv->wm_state_modal) { data[i] = window->display->atom__NET_WM_STATE_MODAL; ++i; } if (window->skip_pager) { data[i] = window->display->atom__NET_WM_STATE_SKIP_PAGER; ++i; } if (window->skip_taskbar) { data[i] = window->display->atom__NET_WM_STATE_SKIP_TASKBAR; ++i; } if (window->maximized_horizontally) { data[i] = window->display->atom__NET_WM_STATE_MAXIMIZED_HORZ; ++i; } if (window->maximized_vertically) { data[i] = window->display->atom__NET_WM_STATE_MAXIMIZED_VERT; ++i; } if (window->fullscreen) { data[i] = window->display->atom__NET_WM_STATE_FULLSCREEN; ++i; } if (!meta_window_showing_on_its_workspace (window) || window->shaded) { data[i] = window->display->atom__NET_WM_STATE_HIDDEN; ++i; } if (window->wm_state_above) { data[i] = window->display->atom__NET_WM_STATE_ABOVE; ++i; } if (window->wm_state_below) { data[i] = window->display->atom__NET_WM_STATE_BELOW; ++i; } if (window->wm_state_demands_attention) { data[i] = window->display->atom__NET_WM_STATE_DEMANDS_ATTENTION; ++i; } if (window->on_all_workspaces_requested) { data[i] = window->display->atom__NET_WM_STATE_STICKY; ++i; } if (meta_window_appears_focused (window)) { data[i] = window->display->atom__NET_WM_STATE_FOCUSED; ++i; } meta_verbose ("Setting _NET_WM_STATE with %d atoms\n", i); meta_error_trap_push (window->display); XChangeProperty (window->display->xdisplay, window->xwindow, window->display->atom__NET_WM_STATE, XA_ATOM, 32, PropModeReplace, (guchar*) data, i); meta_error_trap_pop (window->display); if (window->fullscreen) { if (meta_window_has_fullscreen_monitors (window)) { data[0] = meta_screen_logical_monitor_to_xinerama_index (window->screen, window->fullscreen_monitors.top); data[1] = meta_screen_logical_monitor_to_xinerama_index (window->screen, window->fullscreen_monitors.bottom); data[2] = meta_screen_logical_monitor_to_xinerama_index (window->screen, window->fullscreen_monitors.left); data[3] = meta_screen_logical_monitor_to_xinerama_index (window->screen, window->fullscreen_monitors.right); meta_verbose ("Setting _NET_WM_FULLSCREEN_MONITORS\n"); meta_error_trap_push (window->display); XChangeProperty (window->display->xdisplay, window->xwindow, window->display->atom__NET_WM_FULLSCREEN_MONITORS, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 4); meta_error_trap_pop (window->display); } else { meta_verbose ("Clearing _NET_WM_FULLSCREEN_MONITORS\n"); meta_error_trap_push (window->display); XDeleteProperty (window->display->xdisplay, window->xwindow, window->display->atom__NET_WM_FULLSCREEN_MONITORS); meta_error_trap_pop (window->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) { 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_DISPLAY_HAS_SHAPE (window->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_error_trap_push (window->display); rects = XShapeGetRectangles (window->display->xdisplay, window->xwindow, ShapeInput, &n_rects, &ordering); meta_error_trap_pop (window->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) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); cairo_region_t *region = NULL; if (META_DISPLAY_HAS_SHAPE (window->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_error_trap_push (window->display); XShapeQueryExtents (window->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 (window->display->xdisplay, window->xwindow, ShapeBounding, &n_rects, &ordering); } meta_error_trap_pop (window->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. */ if (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->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, int 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, monitor_rect; rect.x = x; rect.y = y; rect.width = width; rect.height = height; meta_screen_get_monitor_geometry (window->screen, 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->hide_titlebar_when_maximized && (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_display_lookup_x_window (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->xdisplay, event->atom); meta_verbose ("Property notify on %s for %s\n", window->desc, property_name); XFree (property_name); } if (event->atom == window->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_screen (window->screen); 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_display_lookup_x_window (display, event->xclient.window); if (window) { if (event->xclient.data.l[1]) sibling = meta_display_lookup_x_window (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) { 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 == 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 == display->atom__NET_WM_DESKTOP) { int space; MetaWorkspace *workspace; space = event->xclient.data.l[0]; meta_verbose ("Request to move %s to workspace %d\n", window->desc, space); workspace = meta_screen_get_workspace_by_index (window->screen, 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 == 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_error_trap_push (display); str1 = XGetAtomName (display->xdisplay, first); if (meta_error_trap_pop_with_return (display) != Success) str1 = NULL; meta_error_trap_push (display); str2 = XGetAtomName (display->xdisplay, second); if (meta_error_trap_pop_with_return (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 == display->atom__NET_WM_STATE_SHADED || second == 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 == display->atom__NET_WM_STATE_FULLSCREEN || second == 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 == display->atom__NET_WM_STATE_MAXIMIZED_HORZ || second == display->atom__NET_WM_STATE_MAXIMIZED_HORZ || first == display->atom__NET_WM_STATE_MAXIMIZED_VERT || second == 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 == display->atom__NET_WM_STATE_MAXIMIZED_HORZ || second == display->atom__NET_WM_STATE_MAXIMIZED_HORZ) directions |= META_MAXIMIZE_HORIZONTAL; if (first == display->atom__NET_WM_STATE_MAXIMIZED_VERT || second == 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 == display->atom__NET_WM_STATE_MODAL || second == 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 == display->atom__NET_WM_STATE_SKIP_PAGER || second == 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 == display->atom__NET_WM_STATE_SKIP_TASKBAR || second == 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 == display->atom__NET_WM_STATE_ABOVE || second == 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 == display->atom__NET_WM_STATE_BELOW || second == 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 == display->atom__NET_WM_STATE_DEMANDS_ATTENTION || second == 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 == display->atom__NET_WM_STATE_STICKY || second == 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 == 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 == 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->screen, 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 == display->atom__NET_MOVERESIZE_WINDOW) { int gravity; guint value_mask; gravity = (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 == 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 == 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_screen_xinerama_index_to_logical_monitor (window->screen, event->xclient.data.l[0]); bottom = meta_screen_xinerama_index_to_logical_monitor (window->screen, event->xclient.data.l[1]); left = meta_screen_xinerama_index_to_logical_monitor (window->screen, event->xclient.data.l[2]); right = meta_screen_xinerama_index_to_logical_monitor (window->screen, 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 == 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 == 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]; /* Ukwm 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_error_trap_push (display); XChangeProperty (display->xdisplay, xwindow, display->atom_WM_STATE, display->atom_WM_STATE, 32, PropModeReplace, (guchar*) data, 2); meta_error_trap_pop (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 UKWM_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 ("UKWM_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_error_trap_push (display); success = XGetClassHint (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 * Ukwm 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, xwindow, display->atom_WM_STATE, 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->xdisplay, xwindow); } meta_error_trap_pop (display); return filtered; } static gboolean is_our_xwindow (MetaDisplay *display, MetaScreen *screen, Window xwindow, XWindowAttributes *attrs) { if (xwindow == screen->no_focus_window) return TRUE; if (xwindow == screen->wm_sn_selection_window) return TRUE; if (xwindow == screen->wm_cm_selection_window) return TRUE; if (xwindow == screen->guard_window) return TRUE; if (xwindow == screen->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) { MetaScreen *screen = display->screen; XWindowAttributes attrs; gulong existing_wm_state; MetaWindow *window = NULL; gulong event_mask; meta_verbose ("Attempting to manage 0x%lx\n", xwindow); if (meta_display_xwindow_is_a_no_focus_window (display, xwindow)) { meta_verbose ("Not managing no_focus_window 0x%lx\n", xwindow); return NULL; } meta_error_trap_push (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 (display->xdisplay, xwindow, &attrs)) { meta_verbose ("Failed to get attributes for window 0x%lx\n", xwindow); goto error; } if (attrs.root != screen->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 (display, screen, 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 (display, xwindow, display->atom_WM_STATE, 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 Ukwm 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 (display->xdisplay, xwindow); meta_error_trap_push (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 (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 (display->xdisplay, xwindow, &mask, 1); } if (META_DISPLAY_HAS_SHAPE (display)) XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask); /* Get rid of any borders */ if (attrs.border_width != 0) XSetWindowBorderWidth (display->xdisplay, xwindow, 0); /* Get rid of weird gravities */ if (attrs.win_gravity != NorthWestGravity) { XSetWindowAttributes set_attrs; set_attrs.win_gravity = NorthWestGravity; XChangeWindowAttributes (display->xdisplay, xwindow, CWWinGravity, &set_attrs); } if (meta_error_trap_pop_with_return (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, screen, 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_error_trap_pop (display); /* pop the XSync()-reducing trap */ return window; error: meta_error_trap_pop (display); return NULL; } void meta_window_x11_recalc_window_type (MetaWindow *window) { 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 == window->display->atom__NET_WM_WINDOW_TYPE_DESKTOP) type = META_WINDOW_DESKTOP; else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_DOCK) type = META_WINDOW_DOCK; else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_TOOLBAR) type = META_WINDOW_TOOLBAR; else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_MENU) type = META_WINDOW_MENU; else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_UTILITY) type = META_WINDOW_UTILITY; else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_SPLASH) type = META_WINDOW_SPLASHSCREEN; else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_DIALOG) type = META_WINDOW_DIALOG; else if (priv->type_atom == window->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 == window->display->atom__NET_WM_WINDOW_TYPE_DROPDOWN_MENU) type = META_WINDOW_DROPDOWN_MENU; else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_POPUP_MENU) type = META_WINDOW_POPUP_MENU; else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_TOOLTIP) type = META_WINDOW_TOOLTIP; else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_NOTIFICATION) type = META_WINDOW_NOTIFICATION; else if (priv->type_atom == window->display->atom__NET_WM_WINDOW_TYPE_COMBO) type = META_WINDOW_COMBO; else if (priv->type_atom == window->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_error_trap_push (window->display); atom_name = XGetAtomName (window->display->xdisplay, priv->type_atom); meta_error_trap_pop (window->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, FALSE); /* Whether an override-redirect window is considered fullscreen depends * on its geometry. */ if (window->override_redirect) meta_screen_queue_check_fullscreen (window->screen); 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) { #define MAX_N_ACTIONS 12 unsigned long data[MAX_N_ACTIONS]; int i; i = 0; if (window->has_move_func) { data[i] = window->display->atom__NET_WM_ACTION_MOVE; ++i; } if (window->has_resize_func) { data[i] = window->display->atom__NET_WM_ACTION_RESIZE; ++i; } if (window->has_fullscreen_func) { data[i] = window->display->atom__NET_WM_ACTION_FULLSCREEN; ++i; } if (window->has_minimize_func) { data[i] = window->display->atom__NET_WM_ACTION_MINIMIZE; ++i; } if (window->has_shade_func) { data[i] = window->display->atom__NET_WM_ACTION_SHADE; ++i; } /* sticky according to EWMH is different from ukwm's sticky; * ukwm doesn't support EWMH sticky */ if (window->has_maximize_func) { data[i] = window->display->atom__NET_WM_ACTION_MAXIMIZE_HORZ; ++i; data[i] = window->display->atom__NET_WM_ACTION_MAXIMIZE_VERT; ++i; } /* We always allow this */ data[i] = window->display->atom__NET_WM_ACTION_CHANGE_DESKTOP; ++i; if (window->has_close_func) { data[i] = window->display->atom__NET_WM_ACTION_CLOSE; ++i; } /* I guess we always allow above/below operations */ data[i] = window->display->atom__NET_WM_ACTION_ABOVE; ++i; data[i] = window->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_error_trap_push (window->display); XChangeProperty (window->display->xdisplay, window->xwindow, window->display->atom__NET_WM_ALLOWED_ACTIONS, XA_ATOM, 32, PropModeReplace, (guchar*) data, i); meta_error_trap_pop (window->display); #undef MAX_N_ACTIONS } void meta_window_x11_create_sync_request_alarm (MetaWindow *window) { XSyncAlarmAttributes values; XSyncValue init; if (window->sync_request_counter == None || window->sync_request_alarm != None) return; meta_error_trap_push (window->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(window->display->xdisplay, window->sync_request_counter, &init)) { meta_error_trap_pop_with_return (window->display); window->sync_request_counter = None; return; } window->sync_request_serial = XSyncValueLow32 (init) + ((gint64)XSyncValueHigh32 (init) << 32); } else { XSyncIntToValue (&init, 0); XSyncSetCounter (window->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 (window->display->xdisplay, XSyncCACounter | XSyncCAValueType | XSyncCAValue | XSyncCATestType | XSyncCADelta | XSyncCAEvents, &values); if (meta_error_trap_pop_with_return (window->display) == Success) meta_display_register_sync_alarm (window->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) { if (window->sync_request_alarm != None) { /* Has to be unregistered _before_ clearing the structure field */ meta_display_unregister_sync_alarm (window->display, window->sync_request_alarm); XSyncDestroyAlarm (window->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 (window == window->display->grab_window && meta_grab_op_is_resizing (window->display->grab_op) && new_counter_value >= window->sync_request_wait_serial && (!window->extended_sync_request_counter || new_counter_value % 2 == 0) && window->sync_request_timeout_id) { 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); g_source_remove (window->sync_request_timeout_id); window->sync_request_timeout_id = 0; /* 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; } ukwm/src/x11/iconcache.h0000664000175000017500000000505213220600404013741 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm window icons */ /* * Copyright (C) 2002 Havoc Pennington * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_ICON_CACHE_H #define META_ICON_CACHE_H #include "screen-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, MetaDisplay *display, Atom atom); gboolean meta_icon_cache_get_icon_invalidated (MetaIconCache *icon_cache); gboolean meta_read_icons (MetaScreen *screen, 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 ukwm/src/x11/group-props.h0000664000175000017500000000255213220600404014324 0ustar fengfeng/* -*- 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 #include "window-private.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_display_init_group_prop_hooks (MetaDisplay *display); void meta_display_free_group_prop_hooks (MetaDisplay *display); #endif /* META_GROUP_PROPS_H */ ukwm/src/x11/xprops.h0000664000175000017500000001465213220600404013366 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm X property convenience routines */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_XPROPS_H #define META_XPROPS_H #include #include #include #include /* 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 (MetaDisplay *display, Window xwindow, Atom xatom, MotifWmHints **hints_p); gboolean meta_prop_get_cardinal_list (MetaDisplay *display, Window xwindow, Atom xatom, uint32_t **cardinals_p, int *n_cardinals_p); gboolean meta_prop_get_latin1_string (MetaDisplay *display, Window xwindow, Atom xatom, char **str_p); gboolean meta_prop_get_utf8_list (MetaDisplay *display, Window xwindow, Atom xatom, char ***str_p, int *n_str_p); void meta_prop_set_utf8_string_hint (MetaDisplay *display, Window xwindow, Atom atom, const char *val); gboolean meta_prop_get_window (MetaDisplay *display, Window xwindow, Atom xatom, Window *window_p); gboolean meta_prop_get_cardinal (MetaDisplay *display, Window xwindow, Atom xatom, uint32_t *cardinal_p); gboolean meta_prop_get_cardinal_with_atom_type (MetaDisplay *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 (MetaDisplay *display, Window xwindow, MetaPropValue *values, int n_values); void meta_prop_free_values (MetaPropValue *values, int n_values); #endif ukwm/src/x11/group-private.h0000664000175000017500000000213413220600404014627 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm window group private header */ /* * Copyright (C) 2002 Red Hat Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_GROUP_PRIVATE_H #define META_GROUP_PRIVATE_H #include struct _MetaGroup { int refcount; MetaDisplay *display; GSList *windows; Window group_leader; char *startup_id; char *wm_client_machine; }; #endif ukwm/src/Makefile-tests.am0000664000175000017500000000522413220600404014440 0ustar fengfeng# A framework for running scripted tests if HAVE_WAYLAND test_programs = \ ukwm-test-client \ ukwm-test-runner \ ukwm-test-unit-tests \ ukwm-test-headless-start-test \ $(NULL) if BUILDOPT_INSTALL_TESTS stackingdir = $(pkgdatadir)/tests/stacking dist_stacking_DATA = \ tests/stacking/basic-x11.metatest \ tests/stacking/basic-wayland.metatest \ tests/stacking/minimized.metatest \ tests/stacking/mixed-windows.metatest \ tests/stacking/set-parent.metatest \ tests/stacking/override-redirect.metatest ukwm-all.test: tests/ukwm-all.test.in $(AM_V_GEN) sed -e "s|@libexecdir[@]|$(libexecdir)|g" $< > $@.tmp && mv $@.tmp $@ installedtestsdir = $(datadir)/installed-tests/ukwm installedtests_DATA = ukwm-all.test installedtestsbindir = $(libexecdir)/installed-tests/ukwm installedtestsbin_PROGRAMS = $(test_programs) else noinst_PROGRAMS += $(test_programs) endif EXTRA_DIST += tests/ukwm-all.test.in ukwm_test_client_SOURCES = tests/test-client.c ukwm_test_client_LDADD = $(UKWM_LIBS) libukwm-$(LIBUKWM_API_VERSION).la ukwm_test_runner_SOURCES = \ tests/test-utils.c \ tests/test-utils.h \ tests/test-runner.c ukwm_test_runner_LDADD = $(UKWM_LIBS) libukwm-$(LIBUKWM_API_VERSION).la ukwm_test_unit_tests_SOURCES = \ tests/test-utils.c \ tests/test-utils.h \ tests/unit-tests.c \ tests/meta-backend-test.c \ tests/meta-backend-test.h \ tests/meta-monitor-manager-test.c \ tests/meta-monitor-manager-test.h \ tests/monitor-config-migration-unit-tests.c \ tests/monitor-config-migration-unit-tests.h \ tests/monitor-store-unit-tests.c \ tests/monitor-store-unit-tests.h \ tests/monitor-test-utils.c \ tests/monitor-test-utils.h \ tests/monitor-unit-tests.c \ tests/monitor-unit-tests.h \ $(NULL) ukwm_test_unit_tests_LDADD = $(UKWM_LIBS) libukwm-$(LIBUKWM_API_VERSION).la ukwm_test_headless_start_test_SOURCES = \ tests/headless-start-test.c \ tests/meta-backend-test.c \ tests/meta-backend-test.h \ tests/meta-monitor-manager-test.c \ tests/meta-monitor-manager-test.h \ $(NULL) ukwm_test_headless_start_test_LDADD = $(UKWM_LIBS) libukwm-$(LIBUKWM_API_VERSION).la .PHONY: run-tests run-test-runner-tests run-unit-tests run-headless-start-test run-test-runner-tests: ukwm-test-client ukwm-test-runner ./ukwm-test-runner $(dist_stacking_DATA) run-unit-tests: ukwm-test-unit-tests ./ukwm-test-unit-tests run-headless-start-test: ukwm-test-headless-start-test ./ukwm-test-headless-start-test run-tests: run-test-runner-tests run-unit-tests run-headless-start-test endif # Some random test programs for bits of the code testboxes_SOURCES = core/testboxes.c testboxes_LDADD = $(UKWM_LIBS) libukwm-$(LIBUKWM_API_VERSION).la noinst_PROGRAMS += testboxes ukwm/src/tests/0000775000175000017500000000000013254604522012417 5ustar fengfengukwm/src/tests/meta-backend-test.c0000664000175000017500000000266713220600404016052 0ustar fengfeng/* -*- 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-monitor-manager-test.h" struct _MetaBackendTest { MetaBackendX11Nested parent; }; G_DEFINE_TYPE (MetaBackendTest, meta_backend_test, META_TYPE_BACKEND_X11_NESTED) static void meta_backend_test_init (MetaBackendTest *backend_test) { } static MetaMonitorManager * meta_backend_test_create_monitor_manager (MetaBackend *backend) { return g_object_new (META_TYPE_MONITOR_MANAGER_TEST, NULL); } static void meta_backend_test_class_init (MetaBackendTestClass *klass) { MetaBackendClass *backend_class = META_BACKEND_CLASS (klass); backend_class->create_monitor_manager = meta_backend_test_create_monitor_manager; } ukwm/src/tests/meta-backend-test.h0000664000175000017500000000210613220600404016043 0ustar fengfeng/* -*- 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) #endif /* META_BACKEND_TEST_H */ ukwm/src/tests/monitor-unit-tests.h0000664000175000017500000000167513220600404016371 0ustar fengfeng/* -*- 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 */ ukwm/src/tests/stacking/0000775000175000017500000000000013220600404014206 5ustar fengfengukwm/src/tests/stacking/set-parent.metatest0000664000175000017500000000026513220600404020043 0ustar fengfengnew_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 ukwm/src/tests/stacking/mixed-windows.metatest0000664000175000017500000000047313220600404020560 0ustar fengfengnew_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 ukwm/src/tests/stacking/override-redirect.metatest0000664000175000017500000000033413220600404021374 0ustar fengfengnew_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 ukwm/src/tests/stacking/basic-x11.metatest0000664000175000017500000000040213220600404017442 0ustar fengfengnew_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 ukwm/src/tests/stacking/minimized.metatest0000664000175000017500000000050013220600404017736 0ustar fengfengnew_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 ukwm/src/tests/stacking/basic-wayland.metatest0000664000175000017500000000065013220600404020475 0ustar fengfengnew_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 ukwm/src/tests/test-utils.h0000664000175000017500000000511013220600404014666 0ustar fengfeng/* -*- 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 (AsyncWaiter *waiter, MetaDisplay *display, XSyncAlarmNotifyEvent *event); 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 (TestClient *client, MetaDisplay *display, XSyncAlarmNotifyEvent *event); 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); 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); #endif /* TEST_UTILS_H */ ukwm/src/tests/ukwm-all.test.in0000664000175000017500000000017013220600404015440 0ustar fengfeng[Test] Description=All Ukwm tests Exec=@libexecdir@/installed-tests/ukwm/ukwm-test-runner --all Type=session Output=TAP ukwm/src/tests/test-runner.c0000664000175000017500000005245213220600404015045 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat, Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include #include #include #include #include #include #include #include #include "meta-plugin-manager.h" #include "wayland/meta-wayland.h" #include "window-private.h" #include "tests/test-utils.h" typedef struct { GHashTable *clients; AsyncWaiter *waiter; guint log_handler_id; GString *warning_messages; GMainLoop *loop; } TestCase; static gboolean test_case_alarm_filter (MetaDisplay *display, XSyncAlarmNotifyEvent *event, gpointer data) { TestCase *test = data; GHashTableIter iter; gpointer key, value; if (async_waiter_alarm_filter (test->waiter, display, event)) return TRUE; g_hash_table_iter_init (&iter, test->clients); while (g_hash_table_iter_next (&iter, &key, &value)) if (test_client_alarm_filter (value, display, event)) return TRUE; return FALSE; } static gboolean test_case_check_warnings (TestCase *test, GError **error) { if (test->warning_messages != NULL) { g_set_error (error, TEST_RUNNER_ERROR, TEST_RUNNER_ERROR_RUNTIME_ERROR, "Warning messages:\n %s", test->warning_messages->str); g_string_free (test->warning_messages, TRUE); test->warning_messages = NULL; return FALSE; } return TRUE; } static void test_case_log_func (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { TestCase *test = user_data; if (test->warning_messages == NULL) test->warning_messages = g_string_new (message); else { g_string_append (test->warning_messages, "\n "); g_string_append (test->warning_messages, message); } } static TestCase * test_case_new (void) { TestCase *test = g_new0 (TestCase, 1); test->log_handler_id = g_log_set_handler ("ukwm", G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING, test_case_log_func, test); meta_display_set_alarm_filter (meta_get_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_before_redraw (gpointer data) { TestCase *test = data; g_main_loop_quit (test->loop); return FALSE; } 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. * 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_before_redraw, test, NULL); g_main_loop_run (test->loop); /* 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; } #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->screen->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->screen->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->screen->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_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->screen->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->xdisplay, meta_screen_get_xroot (display->screen), &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; } else if (strcmp (argv[0], "set_parent") == 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, "set_parent", window_id, argv[2], NULL)) return FALSE; } else if (strcmp (argv[0], "show") == 0 || 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], "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 { BAD_COMMAND("Unknown command %s", argv[0]); } return test_case_check_warnings (test, error); } 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; if (!test_case_check_warnings (test, 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_display_set_alarm_filter (meta_get_display (), NULL, NULL); g_hash_table_destroy (test->clients); g_free (test); g_log_remove_handler ("ukwm", test->log_handler_id); 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 (UKWM_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 ukwm 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 ("ukwm: %s\n", error->message); exit (1); } g_option_context_free (ctx); meta_plugin_manager_load ("default"); meta_wayland_override_display_name ("ukwm-test-display"); 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 (); } ukwm/src/tests/meta-monitor-manager-test.c0000664000175000017500000003441413220600404017555 0ustar fengfeng/* -*- 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-monitor-config-manager.h" struct _MetaMonitorManagerTest { MetaMonitorManager parent; gboolean is_lid_closed; 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_is_lid_closed (MetaMonitorManagerTest *manager_test, gboolean is_lid_closed) { manager_test->is_lid_closed = is_lid_closed; } 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; } static void meta_monitor_manager_test_read_current (MetaMonitorManager *manager) { MetaMonitorManagerTest *manager_test = META_MONITOR_MANAGER_TEST (manager); g_assert (manager_test->test_setup); manager->modes = manager_test->test_setup->modes; manager->n_modes = manager_test->test_setup->n_modes; manager->crtcs = manager_test->test_setup->crtcs; manager->n_crtcs = manager_test->test_setup->n_crtcs; manager->outputs = manager_test->test_setup->outputs; manager->n_outputs = manager_test->test_setup->n_outputs; } static gboolean meta_monitor_manager_test_is_lid_closed (MetaMonitorManager *manager) { MetaMonitorManagerTest *manager_test = META_MONITOR_MANAGER_TEST (manager); return manager_test->is_lid_closed; } 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) { 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) { crtc->rect.x = 0; crtc->rect.y = 0; crtc->rect.width = 0; crtc->rect.height = 0; crtc->current_mode = NULL; } else { MetaCrtcMode *mode; MetaOutput *output; unsigned int j; int width, height; mode = crtc_info->mode; if (meta_monitor_transform_is_rotated (crtc_info->transform)) { width = mode->height; height = mode->width; } else { width = mode->width; height = mode->height; } crtc->rect.x = crtc_info->x; crtc->rect.y = crtc_info->y; crtc->rect.width = width; crtc->rect.height = height; crtc->current_mode = mode; crtc->transform = crtc_info->transform; for (j = 0; j < crtc_info->outputs->len; j++) { output = ((MetaOutput**)crtc_info->outputs->pdata)[j]; output->is_dirty = TRUE; output->crtc = 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 (i = 0; i < manager->n_crtcs; i++) { MetaCrtc *crtc = &manager->crtcs[i]; crtc->logical_monitor = NULL; if (crtc->is_dirty) { crtc->is_dirty = FALSE; continue; } crtc->rect.x = 0; crtc->rect.y = 0; crtc->rect.width = 0; crtc->rect.height = 0; crtc->current_mode = NULL; } /* Disable outputs not mentioned in the list */ for (i = 0; i < manager->n_outputs; i++) { MetaOutput *output = &manager->outputs[i]; if (output->is_dirty) { output->is_dirty = FALSE; continue; } output->crtc = NULL; 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, 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: 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 = META_MONITOR_MANAGER_CAPABILITY_NONE; capabilities |= META_MONITOR_MANAGER_CAPABILITY_MIRRORING; 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->read_current = meta_monitor_manager_test_read_current; manager_class->is_lid_closed = meta_monitor_manager_test_is_lid_closed; 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; } ukwm/src/tests/monitor-config-migration-unit-tests.c0000664000175000017500000001131613220600404021607 0ustar fengfeng/* -*- 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 (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_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); } ukwm/src/tests/unit-tests.c0000664000175000017500000001762613220600404014702 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat, Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, 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/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/test-utils.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_adjecent_to (void) { MetaRectangle base = { .x = 10, .y = 10, .width = 10, .height = 10 }; MetaRectangle adjecent[] = { { .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_adjecent[] = { { .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 (adjecent); i++) g_assert (meta_rectangle_is_adjecent_to (&base, &adjecent[i])); for (i = 0; i < G_N_ELEMENTS (not_adjecent); i++) g_assert (!meta_rectangle_is_adjecent_to (&base, ¬_adjecent[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 (); ret = g_test_run (); finish_monitor_tests (); meta_quit (ret != 0); return FALSE; } static void init_tests (int argc, char **argv) { g_test_init (&argc, &argv, NULL); g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id="); 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/adjecent-to", meta_test_adjecent_to); init_monitor_store_tests (); init_monitor_config_migration_tests (); init_monitor_tests (); } int main (int argc, char *argv[]) { test_init (argc, argv); init_tests (argc, argv); meta_plugin_manager_load ("default"); meta_override_compositor_configuration (META_COMPOSITOR_TYPE_WAYLAND, META_TYPE_BACKEND_TEST); meta_wayland_override_display_name ("ukwm-test-display"); meta_init (); meta_register_with_session (); g_idle_add (run_tests, NULL); return meta_run (); } ukwm/src/tests/monitor-test-utils.h0000664000175000017500000000177513220600404016370 0ustar fengfeng/* -*- 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 */ ukwm/src/tests/test-client.c0000664000175000017500000002532113220600404015005 0ustar fengfeng/* -*- 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 #include #include #include #include #include #include const char *client_id = "0"; static gboolean wayland; GHashTable *windows; static void read_next_line (GDataInputStream *in); 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; } static void on_after_paint (GdkFrameClock *clock, GMainLoop *loop) { g_signal_handlers_disconnect_by_func (clock, (gpointer) on_after_paint, loop); g_main_loop_quit (loop); } 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); 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: menu "); 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], "show") == 0) { if (argc != 2) { g_print ("usage: show "); goto out; } GtkWidget *window = lookup_window (argv[1]); GdkWindow *gdk_window = gtk_widget_get_window (window); if (!window) goto out; gtk_widget_show (window); /* When a Wayland client, we cannot be really sure that the window has * been mappable until after we have painted. So, in order to have the * test runner rely on the "show" command to have done what the client * needs to do in order for a window to be mappable compositor side, lets * wait with returning until after the first frame. */ GdkFrameClock *frame_clock = gdk_window_get_frame_clock (gdk_window); GMainLoop *loop = g_main_loop_new (NULL, FALSE); g_signal_connect (frame_clock, "after-paint", G_CALLBACK (on_after_paint), loop); g_main_loop_run (loop); g_main_loop_unref (loop); } 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], "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); 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; } ukwm/src/tests/monitor-config-migration-unit-tests.h0000664000175000017500000000167213220600404021620 0ustar fengfeng/* -*- 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 */ ukwm/src/tests/meta-monitor-manager-test.h0000664000175000017500000000410413220600404017553 0ustar fengfeng/* -*- 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 { MetaCrtcMode *modes; int n_modes; MetaOutput *outputs; int n_outputs; MetaCrtc *crtcs; int n_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_emulate_hotplug (MetaMonitorManagerTest *manager_test, MetaMonitorTestSetup *test_setup); void meta_monitor_manager_test_set_is_lid_closed (MetaMonitorManagerTest *manager_test, gboolean is_lid_closed); 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 */ ukwm/src/tests/monitor-test-utils.c0000664000175000017500000000546213220600404016360 0ustar fengfeng/* -*- 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); } ukwm/src/tests/headless-start-test.c0000664000175000017500000001362613220600404016457 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat, Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include "config.h" #include "backends/meta-monitor-manager-private.h" #include "compositor/meta-plugin-manager.h" #include "core/main-private.h" #include "meta/main.h" #include "tests/meta-backend-test.h" #include "tests/meta-monitor-manager-test.h" #include "wayland/meta-wayland.h" #define ALL_TRANSFORMS ((1 << (META_MONITOR_TRANSFORM_FLIPPED_270 + 1)) - 1) 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); ret = g_test_run (); meta_quit (ret != 0); return FALSE; } static void meta_test_headless_start (void) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); g_assert_cmpint ((int) monitor_manager->n_modes, ==, 0); g_assert_cmpint ((int) monitor_manager->n_outputs, ==, 0); g_assert_cmpint ((int) monitor_manager->n_crtcs, ==, 0); 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; MetaScreen *screen; int index; display = meta_get_display (); screen = display->screen; index = meta_screen_get_monitor_index_for_rect (screen, &(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; MetaCrtc **possible_crtcs; GList *logical_monitors; ClutterActor *stage; test_setup = g_new0 (MetaMonitorTestSetup, 1); test_setup->n_modes = 1; test_setup->modes = g_new0 (MetaCrtcMode, test_setup->n_modes); test_setup->modes[0] = (MetaCrtcMode) { .mode_id = 1, .width = 1024, .height = 768, .refresh_rate = 60.0 }; test_setup->n_crtcs = 1; test_setup->crtcs = g_new0 (MetaCrtc, test_setup->n_crtcs); test_setup->crtcs[0] = (MetaCrtc) { .crtc_id = 1, .all_transforms = ALL_TRANSFORMS }; modes = g_new0 (MetaCrtcMode *, 1); modes[0] = &test_setup->modes[0]; possible_crtcs = g_new0 (MetaCrtc *, 1); possible_crtcs[0] = &test_setup->crtcs[0]; test_setup->n_outputs = 1; test_setup->outputs = g_new0 (MetaOutput, test_setup->n_outputs); test_setup->outputs[0] = (MetaOutput) { .winsys_id = 1, .name = g_strdup ("DP-1"), .vendor = g_strdup ("MetaProduct's Inc."), .product = g_strdup ("MetaMonitor"), .serial = g_strdup ("0x987654"), .preferred_mode = modes[0], .n_modes = 1, .modes = modes, .n_possible_crtcs = 1, .possible_crtcs = possible_crtcs, .backlight = -1, .connector_type = META_CONNECTOR_TYPE_DisplayPort }; 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) { g_test_init (&argc, &argv, NULL); g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id="); 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[]) { init_tests (argc, argv); meta_plugin_manager_load ("default"); meta_override_compositor_configuration (META_COMPOSITOR_TYPE_WAYLAND, META_TYPE_BACKEND_TEST); meta_wayland_override_display_name ("ukwm-test-display"); meta_init (); meta_register_with_session (); g_idle_add (run_tests, NULL); return meta_run (); } ukwm/src/tests/monitor-store-unit-tests.h0000664000175000017500000000161613220600404017516 0ustar fengfeng/* -*- 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 */ ukwm/src/tests/monitor-store-unit-tests.c0000664000175000017500000005664313220600404017523 0ustar fengfeng/* -*- 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); } ukwm/src/tests/monitor-unit-tests.c0000664000175000017500000041637113220600404016367 0ustar fengfeng/* -*- 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-logical-monitor.h" #include "backends/meta-monitor.h" #include "backends/meta-monitor-config-migration.h" #include "backends/meta-monitor-config-store.h" #include "tests/meta-monitor-manager-test.h" #include "tests/monitor-test-utils.h" #include "tests/test-utils.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; } 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 { int 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 { long 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; int x; int 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, } }, .n_crtcs = 2, .screen_width = 1024 * 2, .screen_height = 768 } }; static TestClient *monitor_test_client = NULL; #define TEST_CLIENT_NAME "client1" #define TEST_CLIENT_WINDOW "window1" static void create_monitor_test_client (void) { GError *error = NULL; monitor_test_client = test_client_new (TEST_CLIENT_NAME, META_WINDOW_CLIENT_TYPE_WAYLAND, &error); if (!monitor_test_client) g_error ("Failed to launch test client: %s", error->message); if (!test_client_do (monitor_test_client, &error, "create", TEST_CLIENT_WINDOW, NULL)) g_error ("Failed to create window: %s", error->message); if (!test_client_do (monitor_test_client, &error, "show", TEST_CLIENT_WINDOW, NULL)) g_error ("Failed to show the window: %s", error->message); } static void check_monitor_test_client_state (void) { GError *error = NULL; if (!test_client_wait (monitor_test_client, &error)) g_error ("Failed to sync test client: %s", error->message); } static void destroy_monitor_test_client (void) { GError *error = NULL; if (!test_client_quit (monitor_test_client, &error)) g_error ("Failed to quit test client: %s", error->message); test_client_destroy (monitor_test_client); } static MetaOutput * output_from_winsys_id (MetaMonitorManager *monitor_manager, long winsys_id) { unsigned int i; for (i = 0; i < monitor_manager->n_outputs; i++) { MetaOutput *output = &monitor_manager->outputs[i]; if (output->winsys_id == winsys_id) return output; } return NULL; } typedef struct _CheckMonitorModeData { MetaMonitorManager *monitor_manager; 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; MetaMonitorManager *monitor_manager = data->monitor_manager; MetaOutput *output; MetaCrtcMode *crtc_mode; int expect_crtc_mode_index; output = output_from_winsys_id (monitor_manager, data->expect_crtc_mode_iter->output); expect_crtc_mode_index = data->expect_crtc_mode_iter->crtc_mode; if (expect_crtc_mode_index == -1) crtc_mode = NULL; else crtc_mode = &monitor_manager->modes[expect_crtc_mode_index]; g_assert (monitor_crtc_mode->output == output); 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; MetaMonitorManager *monitor_manager = data->monitor_manager; MetaOutput *output; output = output_from_winsys_id (monitor_manager, data->expect_crtc_mode_iter->output); if (data->expect_crtc_mode_iter->crtc_mode == -1) { g_assert_null (output->crtc); } else { MetaLogicalMonitor *logical_monitor; g_assert_nonnull (output->crtc); g_assert (monitor_crtc_mode->crtc_mode == output->crtc->current_mode); logical_monitor = output->crtc->logical_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; if (output->is_primary) { g_assert_null (primary_output); primary_output = output; } g_assert (!output->crtc || output->crtc->logical_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 get_compensated_crtc_position (MetaCrtc *crtc, int *x, int *y) { MetaLogicalMonitor *logical_monitor; MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); GList *views; GList *l; logical_monitor = crtc->logical_monitor; g_assert_nonnull (logical_monitor); views = meta_renderer_get_views (renderer); for (l = views; 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_equal (&view_layout, &logical_monitor->rect)) { *x = crtc->rect.x - view_layout.x; *y = crtc->rect.y - view_layout.y; return; } } *x = crtc->rect.x; *y = crtc->rect.y; } 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); int tiled_monitor_count; GList *monitors; 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) monitor_manager->n_outputs, ==, test_case->expect.n_outputs); g_assert_cmpint ((int) monitor_manager->n_crtcs, ==, 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; long winsys_id = test_case->expect.monitors[i].outputs[j]; g_assert (output == output_from_winsys_id (monitor_manager, 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) { .monitor_manager = monitor_manager, .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) { .monitor_manager = monitor_manager, .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); for (i = 0; i < test_case->expect.n_crtcs; i++) { if (test_case->expect.crtcs[i].current_mode == -1) { g_assert_null (monitor_manager->crtcs[i].current_mode); } else { MetaCrtc *crtc = &monitor_manager->crtcs[i]; MetaLogicalMonitor *logical_monitor = crtc->logical_monitor; MetaCrtcMode *expected_current_mode = &monitor_manager->modes[test_case->expect.crtcs[i].current_mode]; int crtc_x, crtc_y; g_assert (crtc->current_mode == expected_current_mode); g_assert_cmpuint (crtc->transform, ==, test_case->expect.crtcs[i].transform); if (meta_is_stage_views_enabled ()) { get_compensated_crtc_position (crtc, &crtc_x, &crtc_y); g_assert_cmpint (crtc_x, ==, test_case->expect.crtcs[i].x); g_assert_cmpint (crtc_y, ==, test_case->expect.crtcs[i].y); } else { int expect_crtc_x; int expect_crtc_y; g_assert_cmpuint (logical_monitor->transform, ==, crtc->transform); expect_crtc_x = (test_case->expect.crtcs[i].x + logical_monitor->rect.x); expect_crtc_y = (test_case->expect.crtcs[i].y + logical_monitor->rect.y); g_assert_cmpint (crtc->rect.x, ==, expect_crtc_x); g_assert_cmpint (crtc->rect.y, ==, expect_crtc_y); } } } check_monitor_test_client_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->n_modes = test_case->setup.n_modes; test_setup->modes = g_new0 (MetaCrtcMode, test_setup->n_modes); for (i = 0; i < test_setup->n_modes; i++) { test_setup->modes[i] = (MetaCrtcMode) { .mode_id = i, .width = test_case->setup.modes[i].width, .height = test_case->setup.modes[i].height, .refresh_rate = test_case->setup.modes[i].refresh_rate, .flags = test_case->setup.modes[i].flags, }; } test_setup->n_crtcs = test_case->setup.n_crtcs; test_setup->crtcs = g_new0 (MetaCrtc, test_setup->n_crtcs); for (i = 0; i < test_setup->n_crtcs; i++) { int current_mode_index; MetaCrtcMode *current_mode; current_mode_index = test_case->setup.crtcs[i].current_mode; if (current_mode_index == -1) current_mode = NULL; else current_mode = &test_setup->modes[current_mode_index]; test_setup->crtcs[i] = (MetaCrtc) { .crtc_id = i + 1, .current_mode = current_mode, .transform = META_MONITOR_TRANSFORM_NORMAL, .all_transforms = ALL_TRANSFORMS }; } test_setup->n_outputs = test_case->setup.n_outputs; test_setup->outputs = g_new0 (MetaOutput, test_setup->n_outputs); for (i = 0; i < test_setup->n_outputs; i++) { 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 = &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 = &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] = &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] = &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"; test_setup->outputs[i] = (MetaOutput) { .crtc = crtc, .winsys_id = i, .name = (is_laptop_panel ? g_strdup_printf ("eDP-%d", ++n_laptop_panels) : g_strdup_printf ("DP-%d", ++n_normal_panels)), .vendor = g_strdup ("MetaProduct's Inc."), .product = g_strdup ("MetaMonitor"), .serial = g_strdup (serial), .suggested_x = -1, .suggested_y = -1, .hotplug_mode_update = hotplug_mode_update, .width_mm = test_case->setup.outputs[i].width_mm, .height_mm = test_case->setup.outputs[i].height_mm, .subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN, .preferred_mode = preferred_mode, .n_modes = n_modes, .modes = modes, .n_possible_crtcs = n_possible_crtcs, .possible_crtcs = possible_crtcs, .n_possible_clones = 0, .possible_clones = NULL, .backlight = -1, .connector_type = (is_laptop_panel ? META_CONNECTOR_TYPE_eDP : META_CONNECTOR_TYPE_DisplayPort), .tile_info = test_case->setup.outputs[i].tile_info, .is_underscanning = test_case->setup.outputs[i].is_underscanning, .driver_private = output_test, .driver_notify = (GDestroyNotify) meta_output_test_destroy_notify }; } 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, } }, .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, } }, .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 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, }, { .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); test_setup->outputs[0].suggested_x = 1024; test_setup->outputs[0].suggested_y = 758; test_setup->outputs[1].suggested_x = 0; test_setup->outputs[1].suggested_y = 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, } }, .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); MetaMonitorManagerTest *monitor_manager_test = META_MONITOR_MANAGER_TEST (monitor_manager); test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); meta_monitor_manager_test_set_is_lid_closed (monitor_manager_test, 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; check_monitor_configuration (&test_case); meta_monitor_manager_test_set_is_lid_closed (monitor_manager_test, 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; 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); MetaMonitorManagerTest *monitor_manager_test = META_MONITOR_MANAGER_TEST (monitor_manager); test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); meta_monitor_manager_test_set_is_lid_closed (monitor_manager_test, TRUE); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); meta_monitor_manager_test_set_is_lid_closed (monitor_manager_test, 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[1].current_mode = 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 (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerTest *monitor_manager_test = META_MONITOR_MANAGER_TEST (monitor_manager); test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); meta_monitor_manager_test_set_is_lid_closed (monitor_manager_test, TRUE); emulate_hotplug (test_setup); 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; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); /* 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_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, } }, .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, } }, .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 = 400, .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, } }, .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, }, { .current_mode = 0, .transform = META_MONITOR_TRANSFORM_90 } }, .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, }, { .current_mode = 1, .transform = META_MONITOR_TRANSFORM_90, .x = 0, .y = 400, }, { .current_mode = 1, .transform = META_MONITOR_TRANSFORM_90 } }, .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_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, }, { .current_mode = 0, .transform = META_MONITOR_TRANSFORM_NORMAL } }, .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_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, } }, .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 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, } }, .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); MetaMonitorConfigManager *config_manager = monitor_manager->config_manager; 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/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/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-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/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); } void pre_run_monitor_tests (void) { create_monitor_test_client (); } void finish_monitor_tests (void) { destroy_monitor_test_client (); } ukwm/src/tests/test-utils.c0000664000175000017500000002724413220600404014675 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014-2017 Red Hat, Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, 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" 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; void test_init (int argc, char **argv) { char *basename = g_path_get_basename (argv[0]); char *dirname = g_path_get_dirname (argv[0]); if (g_str_has_prefix (basename, "lt-")) test_client_path = g_build_filename (dirname, "../ukwm-test-client", NULL); else test_client_path = g_build_filename (dirname, "ukwm-test-client", NULL); g_free (basename); g_free (dirname); } AsyncWaiter * async_waiter_new (void) { AsyncWaiter *waiter = g_new0 (AsyncWaiter, 1); Display *xdisplay = meta_get_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) { Display *xdisplay = meta_get_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) { Display *xdisplay = meta_get_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 (AsyncWaiter *waiter, MetaDisplay *display, XSyncAlarmNotifyEvent *event) { 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 Ukwm", client->id, window_id); return result; } gboolean test_client_alarm_filter (TestClient *client, MetaDisplay *display, XSyncAlarmNotifyEvent *event) { if (client->waiter) return async_waiter_alarm_filter (client->waiter, display, event); else return FALSE; } TestClient * test_client_new (const char *id, MetaWindowClientType type, GError **error) { TestClient *client = g_new0 (TestClient, 1); 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->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); } ukwm/src/org.ukui.ukwm.IdleMonitor.xml0000664000175000017500000000167013220600404016740 0ustar fengfeng ukwm/src/ui/0000775000175000017500000000000013254604522011672 5ustar fengfengukwm/src/ui/frames.h0000664000175000017500000001016413220600404013306 0ustar fengfeng/* -*- 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 #include #include "theme-private.h" #include "ui.h" typedef enum { META_FRAME_CONTROL_NONE, META_FRAME_CONTROL_TITLE, META_FRAME_CONTROL_DELETE, META_FRAME_CONTROL_MENU, META_FRAME_CONTROL_APPMENU, 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; }; struct _MetaFrames { GtkWindow parent_instance; 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; }; struct _MetaFramesClass { GtkWindowClass parent_class; }; GType meta_frames_get_type (void) G_GNUC_CONST; MetaFrames *meta_frames_new (void); 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_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 ukwm/src/ui/frames.c0000664000175000017500000014304613220600404013307 0ustar fengfeng/* -*- 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 #include #include #include #include "frames.h" #include #include "core.h" #include #include #include "ui.h" #include "core/window-private.h" #include "core/frame.h" #include "x11/window-x11.h" #include "x11/window-x11-private.h" #include #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); /* For UKUI theme control */ static void meta_frames_theme_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); 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; /* For UKUI theme control */ case META_PREF_THEME: meta_frames_theme_changed (META_FRAMES (data)); break; default: break; } } static void invalidate_whole_window (MetaUIFrame *frame) { 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 data) { MetaUIFrame *frame = value; invalidate_whole_window (frame); meta_core_queue_frame_resize (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), 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); } /* For UKUI theme control */ static void meta_frames_theme_changed (MetaFrames *frames) { GtkWidget *widget; widget = GTK_WIDGET(frames); g_signal_emit_by_name(widget,"style-updated",NULL); } 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_retheme_all (); 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 (void) { MetaFrames *frames; frames = g_object_new (META_TYPE_FRAMES, "type", GTK_WINDOW_POPUP, NULL); /* 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; meta_core_grab_buttons (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), 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_core_set_screen_cursor (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), 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)); 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, ClutterButtonEvent *event, int action) { MetaFrameFlags flags; Display *display; display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); flags = meta_frame_get_flags (frame->meta_window->frame); switch (action) { case G_DESKTOP_TITLEBAR_ACTION_TOGGLE_SHADE: { if (flags & META_FRAME_ALLOWS_SHADE) { if (flags & META_FRAME_SHADED) meta_window_unshade (frame->meta_window, event->time); else meta_window_shade (frame->meta_window, event->time); } } break; case G_DESKTOP_TITLEBAR_ACTION_TOGGLE_MAXIMIZE: { if (flags & META_FRAME_ALLOWS_MAXIMIZE) { meta_core_toggle_maximize (display, frame->xwindow); } } break; case G_DESKTOP_TITLEBAR_ACTION_TOGGLE_MAXIMIZE_HORIZONTALLY: { if (flags & META_FRAME_ALLOWS_MAXIMIZE) { meta_core_toggle_maximize_horizontally (display, frame->xwindow); } } break; case G_DESKTOP_TITLEBAR_ACTION_TOGGLE_MAXIMIZE_VERTICALLY: { if (flags & META_FRAME_ALLOWS_MAXIMIZE) { meta_core_toggle_maximize_vertically (display, frame->xwindow); } } break; case G_DESKTOP_TITLEBAR_ACTION_MINIMIZE: { if (flags & META_FRAME_ALLOWS_MINIMIZE) meta_window_minimize (frame->meta_window); } break; case G_DESKTOP_TITLEBAR_ACTION_NONE: /* Yaay, a sane user that doesn't use that other weird crap! */ break; case G_DESKTOP_TITLEBAR_ACTION_LOWER: meta_core_user_lower_and_unfocus (display, frame->xwindow, event->time); break; case G_DESKTOP_TITLEBAR_ACTION_MENU: meta_core_show_window_menu (display, frame->xwindow, META_WINDOW_MENU_WM, event->x, event->y, event->time); break; } return TRUE; } static gboolean meta_frame_double_click_event (MetaUIFrame *frame, ClutterButtonEvent *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, 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, 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; Display *display; gboolean ret; display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); ret = meta_core_begin_grab_op (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; } return ret; } static gboolean meta_frames_retry_grab_op (MetaFrames *frames, guint time) { Display *display; MetaGrabOp op; if (frames->current_grab_op == META_GRAB_OP_NONE) return TRUE; op = frames->current_grab_op; frames->current_grab_op = META_GRAB_OP_NONE; display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); return meta_core_begin_grab_op (display, frames->grab_frame->xwindow, op, FALSE, TRUE, frames->grab_frame->grab_button, 0, time, frames->grab_x, frames->grab_y); } 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 (); } } static gboolean meta_frame_left_click_event (MetaUIFrame *frame, ClutterButtonEvent *event) { Display *display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); MetaFrameControl control = get_control (frame, event->x, event->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: case META_FRAME_CONTROL_APPMENU: frame->grab_button = event->button; frame->button_state = META_BUTTON_STATE_PRESSED; frame->prelit_control = control; redraw_control (frame, control); if (control == META_FRAME_CONTROL_MENU || control == META_FRAME_CONTROL_APPMENU) { MetaFrameGeometry fgeom; GdkRectangle *rect; MetaRectangle root_rect; MetaWindowMenuType menu; 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; menu = control == META_FRAME_CONTROL_MENU ? META_WINDOW_MENU_WM : META_WINDOW_MENU_APP; /* 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_core_show_window_menu_for_rect (display, frame->xwindow, menu, &root_rect, event->time); } else { meta_frames_try_grab_op (frame, META_GRAB_OP_FRAME_BUTTON, event->x, event->y, event->time); } 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), event->x, event->y, event->time); 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, event->x, event->y, event->time); } } 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 */ return FALSE; default: g_assert_not_reached (); } } static gboolean handle_button_press_event (MetaUIFrame *frame, ClutterButtonEvent *event) { MetaFrameControl control; Display *display; display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); control = get_control (frame, event->x, event->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 (event->button == 1 && !(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, event->time); } /* 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 && event->button == 1 && event->click_count == 2) { meta_core_end_grab_op (display, event->time); return meta_frame_double_click_event (frame, event); } if (meta_core_get_grab_op (display) != META_GRAB_OP_NONE) return FALSE; /* already up to something */ frame->grab_button = event->button; switch (event->button) { case 1: return meta_frame_left_click_event (frame, event); case 2: return meta_frame_middle_click_event (frame, event); case 3: return meta_frame_right_click_event (frame, event); default: return FALSE; } } static gboolean handle_button_release_event (MetaUIFrame *frame, ClutterButtonEvent *event) { Display *display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); frame->frames->current_grab_op = META_GRAB_OP_NONE; meta_core_end_grab_op (display, event->time); /* 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) event->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, event->time); 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, event->time); 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, event->x, event->y); meta_ui_frame_update_prelit_control (frame, control); } return TRUE; } 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_APPMENU: 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_core_set_screen_cursor (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), frame->xwindow, cursor); switch (control) { case META_FRAME_CONTROL_MENU: case META_FRAME_CONTROL_APPMENU: 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_notify_event (MetaUIFrame *frame, ClutterMotionEvent *event) { MetaFrames *frames = frame->frames; MetaFrameControl control; control = get_control (frame, event->x, event->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 ((event->modifier_state & CLUTTER_BUTTON1_MASK) && frames->current_grab_op != META_GRAB_OP_NONE) meta_frames_retry_grab_op (frames, event->time); 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 * @xwindow: The X window for the frame, which has the client window as a child * @cr: Used to draw the resulting mask */ void meta_ui_frame_get_mask (MetaUIFrame *frame, cairo_t *cr) { MetaFrameBorders borders; MetaFrameFlags flags; MetaRectangle frame_rect; int scale = meta_theme_get_window_scaling_factor (); meta_window_get_frame_rect (frame->meta_window, &frame_rect); 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 */ cairo_scale (cr, 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); } /* 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_APPMENU: button_type = META_BUTTON_TYPE_APPMENU; 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); } 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) { Display *display; MetaGrabOp grab_op; display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); grab_op = meta_core_get_grab_op (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) { switch (event->any.type) { case CLUTTER_BUTTON_PRESS: return handle_button_press_event (frame, (ClutterButtonEvent *) event); case CLUTTER_BUTTON_RELEASE: return handle_button_release_event (frame, (ClutterButtonEvent *) event); case CLUTTER_MOTION: return handle_motion_notify_event (frame, (ClutterMotionEvent *) 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_APPMENU: rect = &fgeom->appmenu_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 (POINT_IN_RECT (x, y, client)) return META_FRAME_CONTROL_CLIENT_AREA; meta_ui_frame_calc_geometry (frame, &fgeom); if (POINT_IN_RECT (x, y, fgeom.close_rect.clickable)) return META_FRAME_CONTROL_DELETE; if (POINT_IN_RECT (x, y, fgeom.min_rect.clickable)) return META_FRAME_CONTROL_MINIMIZE; if (POINT_IN_RECT (x, y, fgeom.menu_rect.clickable)) return META_FRAME_CONTROL_MENU; if (POINT_IN_RECT (x, y, fgeom.appmenu_rect.clickable)) return META_FRAME_CONTROL_APPMENU; 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_LEFT || flags & META_FRAME_TILED_RIGHT) has_vert = has_horiz = FALSE; if (POINT_IN_RECT (x, y, fgeom.title_rect)) { if (has_vert && y <= TOP_RESIZE_HEIGHT && has_north_resize) return META_FRAME_CONTROL_RESIZE_N; else return META_FRAME_CONTROL_TITLE; } if (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) return META_FRAME_CONTROL_RESIZE_SE; else if (has_vert) return META_FRAME_CONTROL_RESIZE_S; else if (has_horiz) 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) return META_FRAME_CONTROL_RESIZE_SW; else if (has_vert) return META_FRAME_CONTROL_RESIZE_S; else if (has_horiz) 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) return META_FRAME_CONTROL_RESIZE_NW; else if (has_vert) return META_FRAME_CONTROL_RESIZE_N; else if (has_horiz) 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) return META_FRAME_CONTROL_RESIZE_NE; else if (has_vert) return META_FRAME_CONTROL_RESIZE_N; else if (has_horiz) return META_FRAME_CONTROL_RESIZE_E; } else if (y < (fgeom.borders.invisible.top + TOP_RESIZE_HEIGHT)) { if (has_vert && has_north_resize) return META_FRAME_CONTROL_RESIZE_N; } else if (y >= (fgeom.height - fgeom.borders.total.bottom)) { if (has_vert) return META_FRAME_CONTROL_RESIZE_S; } else if (x <= fgeom.borders.total.left) { if (has_horiz || flags & META_FRAME_TILED_RIGHT) return META_FRAME_CONTROL_RESIZE_W; } else if (x >= (fgeom.width - fgeom.borders.total.right)) { if (has_horiz || flags & META_FRAME_TILED_LEFT) return META_FRAME_CONTROL_RESIZE_E; } if (y >= fgeom.borders.total.top) return META_FRAME_CONTROL_NONE; else return META_FRAME_CONTROL_TITLE; } ukwm/src/ui/theme.c0000664000175000017500000013214613220600404013133 0ustar fengfeng/* -*- 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 #include "theme-private.h" #include "frames.h" /* for META_TYPE_FRAMES */ #include "util-private.h" #include #include #include #include #include #include #define DEBUG_FILL_STRUCT(s) memset ((s), 0xef, sizeof (*(s))) 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 = 1; 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); DEBUG_FILL_STRUCT (layout); 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) { borders->invisible.left = MAX (borders->invisible.left, draggable_borders - borders->visible.left); borders->invisible.right = MAX (borders->invisible.right, draggable_borders - borders->visible.right); } if (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE) { borders->invisible.bottom = MAX (borders->invisible.bottom, draggable_borders - borders->visible.bottom); /* 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) borders->invisible.top = MAX (borders->invisible.top, draggable_borders - 2); } 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_APPMENU: if (flags & META_FRAME_ALLOWS_APPMENU) return &fgeom->appmenu_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 if (strip_button (right_func_rects, &n_right, &fgeom->appmenu_rect)) continue; else if (strip_button (left_func_rects, &n_left, &fgeom->appmenu_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_LEFT || flags & META_FRAME_TILED_RIGHT) { 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 g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable)); x = rect->visible.x - layout->button_margin.left * scale; if (i > 0) x -= layout->titlebar_spacing; --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 g_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; case META_BUTTON_TYPE_APPMENU: *rect = fgeom->appmenu_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; int scale = meta_theme_get_window_scaling_factor (); /* 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 */ cairo_scale (cr, 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; case META_BUTTON_TYPE_APPMENU: surface = cairo_surface_reference (mini_icon); break; default: icon_name = NULL; break; } if (icon_name) { GtkIconTheme *theme = gtk_icon_theme_get_default (); GtkIconInfo *info; GdkPixbuf *pixbuf; info = gtk_icon_theme_lookup_icon_for_scale (theme, icon_name, layout->icon_size, scale, 0); pixbuf = gtk_icon_info_load_symbolic_for_context (info, style, NULL, NULL); surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL); } if (surface) { float width, height; int x, y; width = cairo_image_surface_get_width (surface) / scale; height = cairo_image_surface_get_height (surface) / scale; x = button_rect.x + (button_rect.width - width) / 2; y = button_rect.y + (button_rect.height - height) / 2; cairo_translate (cr, x, y); cairo_scale (cr, width / layout->icon_size, height / layout->icon_size); cairo_set_source_surface (cr, surface, 0, 0); cairo_paint (cr); 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); } } /** * 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]); DEBUG_FILL_STRUCT (theme); 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; } MetaStyleInfo * meta_theme_create_style_info (GdkScreen *screen, const gchar *variant) { MetaStyleInfo *style_info; GtkCssProvider *provider; char *theme_name; /* For UKUI theme control * g_object_get (gtk_settings_get_for_screen (screen), "gtk-theme-name", &theme_name, NULL); */ theme_name = g_strdup(meta_prefs_get_theme()); if (theme_name && *theme_name) provider = gtk_css_provider_get_named (theme_name, variant); else provider = gtk_css_provider_get_default (); 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_IS_FLASHING) backdrop = !backdrop; if (flags & META_FRAME_MAXIMIZED) class_name = "maximized"; else if (flags & META_FRAME_TILED_LEFT || flags & META_FRAME_TILED_RIGHT) 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 ""; } ukwm/src/ui/ui.c0000664000175000017500000002040413220600404012437 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukuim interface for talking to GTK+ UI module */ /* * Copyright (C) 2002 Havoc Pennington * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include #include #include "ui.h" #include "frames.h" #include #include "core.h" #include "theme-private.h" #include #include #include 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; }; void meta_ui_init (void) { const char *gdk_gl_env = NULL; gdk_set_allowed_backends ("x11"); gdk_gl_env = g_getenv ("GDK_GL"); g_setenv("GDK_GL", "disable", TRUE); if (!gtk_init_check (NULL, NULL)) meta_fatal ("Unable to open X display %s\n", XDisplayName (NULL)); 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_get_default (), 1); } Display* meta_ui_get_display (void) { return GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); } gint meta_ui_get_screen_number (void) { return gdk_screen_get_number (gdk_screen_get_default ()); } MetaUI* meta_ui_new (Display *xdisplay) { GdkDisplay *gdisplay; MetaUI *ui; ui = g_new0 (MetaUI, 1); ui->xdisplay = xdisplay; gdisplay = gdk_x11_lookup_xdisplay (xdisplay); g_assert (gdisplay == gdk_display_get_default ()); ui->frames = meta_frames_new (); /* 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 (gdisplay), "meta-ui", ui); return ui; } void meta_ui_free (MetaUI *ui) { GdkDisplay *gdisplay; gtk_widget_destroy (GTK_WIDGET (ui->frames)); gdisplay = gdk_x11_lookup_xdisplay (ui->xdisplay); g_object_set_data (G_OBJECT (gdisplay), "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); } ukwm/src/ui/theme-private.h0000664000175000017500000002104313220600404014601 0ustar fengfeng/* -*- 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 #include #include /** * 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, appmenu_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; MetaButtonSpace appmenu_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_APPMENU, 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 */ ukwm/src/ui/ui.h0000664000175000017500000000541113220600404012445 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Ukwm interface for talking to GTK+ UI module */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_UI_H #define META_UI_H /* Don't include gtk.h or gdk.h here */ #include #include #include #include #include #include #include typedef struct _MetaUI MetaUI; typedef struct _MetaUIFrame MetaUIFrame; typedef gboolean (* MetaEventFunc) (XEvent *xevent, gpointer data); void meta_ui_init (void); Display* meta_ui_get_display (void); gint meta_ui_get_screen_number (void); MetaUI* meta_ui_new (Display *xdisplay); 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); gboolean meta_ui_window_is_dummy (MetaUI *ui, Window xwindow); #endif ukwm/src/backends/0000775000175000017500000000000013260055411013021 5ustar fengfengukwm/src/backends/meta-cursor.h0000664000175000017500000000544113220600404015431 0ustar fengfeng/* -*- 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 #include typedef struct _MetaCursorSprite MetaCursorSprite; #define META_TYPE_CURSOR_SPRITE (meta_cursor_sprite_get_type ()) G_DECLARE_FINAL_TYPE (MetaCursorSprite, meta_cursor_sprite, META, CURSOR_SPRITE, GObject); MetaCursorSprite * meta_cursor_sprite_new (void); MetaCursorSprite * meta_cursor_sprite_from_theme (MetaCursor cursor); void meta_cursor_sprite_set_theme_scale (MetaCursorSprite *self, int scale); MetaCursor meta_cursor_sprite_get_meta_cursor (MetaCursorSprite *self); Cursor meta_cursor_create_x_cursor (Display *xdisplay, MetaCursor cursor); void meta_cursor_sprite_prepare_at (MetaCursorSprite *self, int x, int y); void meta_cursor_sprite_realize_texture (MetaCursorSprite *self); void meta_cursor_sprite_set_texture (MetaCursorSprite *self, CoglTexture *texture, int hot_x, int hot_y); void meta_cursor_sprite_set_texture_scale (MetaCursorSprite *self, float scale); CoglTexture *meta_cursor_sprite_get_cogl_texture (MetaCursorSprite *self); void meta_cursor_sprite_get_hotspot (MetaCursorSprite *self, int *hot_x, int *hot_y); float meta_cursor_sprite_get_texture_scale (MetaCursorSprite *self); gboolean meta_cursor_sprite_is_animated (MetaCursorSprite *self); void meta_cursor_sprite_tick_frame (MetaCursorSprite *self); guint meta_cursor_sprite_get_current_frame_time (MetaCursorSprite *self); #endif /* META_CURSOR_H */ ukwm/src/backends/meta-idle-monitor-dbus.h0000664000175000017500000000176113220600404017452 0ustar fengfeng/* -*- 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 ukwm/src/backends/meta-pointer-constraint.h0000664000175000017500000000411513233511035017760 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #ifndef META_POINTER_CONSTRAINT_H #define META_POINTER_CONSTRAINT_H #include #include 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); 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 */ ukwm/src/backends/meta-monitor-config-store.h0000664000175000017500000000442713233511035020210 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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) MetaMonitorConfigStore * meta_monitor_config_store_new (MetaMonitorManager *monitor_manager); MetaMonitorsConfig * meta_monitor_config_store_lookup (MetaMonitorConfigStore *config_store, MetaMonitorsConfigKey *key); void meta_monitor_config_store_add (MetaMonitorConfigStore *config_store, MetaMonitorsConfig *config); void meta_monitor_config_store_remove (MetaMonitorConfigStore *config_store, MetaMonitorsConfig *config); gboolean meta_monitor_config_store_set_custom (MetaMonitorConfigStore *config_store, const char *read_path, const char *write_path, GError **error); int meta_monitor_config_store_get_config_count (MetaMonitorConfigStore *config_store); MetaMonitorManager * meta_monitor_config_store_get_monitor_manager (MetaMonitorConfigStore *config_store); #endif /* META_MONITOR_CONFIG_STORE_H */ ukwm/src/backends/meta-remote-desktop.c0000664000175000017500000001700013233511035017050 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015-2017 Red Hat Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * */ #define _GNU_SOURCE #include "config.h" #include "backends/meta-remote-desktop.h" #include #include #include #include #include #include "meta-dbus-remote-desktop.h" #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/errors.h" #include "meta/meta-backend.h" #define META_REMOTE_DESKTOP_DBUS_SERVICE "org.ukui.ukwm.RemoteDesktop" #define META_REMOTE_DESKTOP_DBUS_PATH "/org/ukui/ukwm/RemoteDesktop" 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 void meta_remote_desktop_init (MetaRemoteDesktop *remote_desktop) { remote_desktop->sessions = g_hash_table_new (g_str_hash, g_str_equal); } 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; } ukwm/src/backends/meta-monitor-manager.c0000664000175000017500000030453313220600404017212 0ustar fengfeng/* -*- 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. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include "config.h" #include "meta-monitor-manager-private.h" #include #include #include #include #include #include "util-private.h" #include #include "edid.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/x11/meta-monitor-manager-xrandr.h" #include "meta-backend-private.h" #define DEFAULT_DISPLAY_CONFIGURATION_TIMEOUT 20 enum { MONITORS_CHANGED_INTERNAL, 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]; static void meta_monitor_manager_display_config_init (MetaDBusDisplayConfigIface *iface); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MetaMonitorManager, meta_monitor_manager, META_DBUS_TYPE_DISPLAY_CONFIG_SKELETON, G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_DISPLAY_CONFIG, meta_monitor_manager_display_config_init)); static void initialize_dbus_interface (MetaMonitorManager *manager); static gboolean meta_monitor_manager_is_config_complete (MetaMonitorManager *manager, MetaMonitorsConfig *config); static void cleanup_pending_cleanup_state (MetaMonitorManager *manager); 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); } static float derive_configured_global_scale (MetaMonitorManager *manager, MetaMonitorsConfig *config) { MetaLogicalMonitorConfig *logical_monitor_config; logical_monitor_config = config->logical_monitor_configs->data; return logical_monitor_config->scale; } 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, monitor, monitor_mode); } static float derive_calculated_global_scale (MetaMonitorManager *manager) { MetaMonitor *primary_monitor; primary_monitor = meta_monitor_manager_get_primary_monitor (manager); if (!primary_monitor) return 1.0; return calculate_monitor_scale (manager, primary_monitor); } 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 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 = global_scale; else 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); } static void power_save_mode_changed (MetaMonitorManager *manager, GParamSpec *pspec, gpointer user_data) { MetaMonitorManagerClass *klass; int mode = meta_dbus_display_config_get_power_save_mode (META_DBUS_DISPLAY_CONFIG (manager)); if (mode == META_POWER_SAVE_UNSUPPORTED) return; /* If DPMS is unsupported, force the property back. */ if (manager->power_save_mode == META_POWER_SAVE_UNSUPPORTED) { meta_dbus_display_config_set_power_save_mode (META_DBUS_DISPLAY_CONFIG (manager), 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); manager->power_save_mode = mode; } void meta_monitor_manager_lid_is_closed_changed (MetaMonitorManager *manager) { meta_monitor_manager_ensure_configured (manager); } static void lid_is_closed_changed (UpClient *client, GParamSpec *pspec, gpointer user_data) { MetaMonitorManager *manager = user_data; meta_monitor_manager_lid_is_closed_changed (manager); } static gboolean meta_monitor_manager_real_is_lid_closed (MetaMonitorManager *manager) { if (!manager->up_client) return FALSE; return up_client_get_lid_is_closed (manager->up_client); } gboolean meta_monitor_manager_is_lid_closed (MetaMonitorManager *manager) { return META_MONITOR_MANAGER_GET_CLASS (manager)->is_lid_closed (manager); } gboolean meta_monitor_manager_is_headless (MetaMonitorManager *manager) { return !manager->logical_monitors; } float meta_monitor_manager_calculate_monitor_mode_scale (MetaMonitorManager *manager, MetaMonitor *monitor, MetaMonitorMode *monitor_mode) { MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_GET_CLASS (manager); return manager_class->calculate_monitor_mode_scale (manager, monitor, monitor_mode); } 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); } 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) { unsigned int i; for (i = 0; i < manager->n_outputs; i++) { MetaOutput *output = &manager->outputs[i]; if (output->hotplug_mode_update) 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; 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) { config = meta_monitor_config_manager_get_stored (manager->config_manager); 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 (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 void experimental_features_changed (MetaSettings *settings, MetaExperimentalFeature old_experimental_features, MetaMonitorManager *manager) { gboolean was_stage_views_scaled; gboolean is_stage_views_scaled; 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); if (is_stage_views_scaled != was_stage_views_scaled) should_reconfigure = TRUE; if (should_reconfigure) meta_monitor_manager_on_hotplug (manager); meta_settings_update_ui_scaling_factor (settings); } static void meta_monitor_manager_constructed (GObject *object) { MetaMonitorManager *manager = META_MONITOR_MANAGER (object); MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_GET_CLASS (manager); MetaBackend *backend = meta_get_backend (); MetaSettings *settings = meta_backend_get_settings (backend); manager->experimental_features_changed_handler_id = g_signal_connect (settings, "experimental-features-changed", G_CALLBACK (experimental_features_changed), manager); if (manager_class->is_lid_closed == meta_monitor_manager_real_is_lid_closed) { manager->up_client = up_client_new (); g_signal_connect_object (manager->up_client, "notify::lid-is-closed", G_CALLBACK (lid_is_closed_changed), manager, 0); } g_signal_connect_object (manager, "notify::power-save-mode", G_CALLBACK (power_save_mode_changed), manager, 0); g_signal_connect_object (meta_backend_get_orientation_manager (backend), "orientation-changed", G_CALLBACK (orientation_changed), manager, 0); manager->current_switch_config = META_MONITOR_SWITCH_CONFIG_UNKNOWN; 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); initialize_dbus_interface (manager); manager->in_init = FALSE; } void meta_monitor_manager_clear_output (MetaOutput *output) { 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); memset (output, 0, sizeof (*output)); } static void meta_monitor_manager_free_output_array (MetaOutput *old_outputs, int n_old_outputs) { int i; for (i = 0; i < n_old_outputs; i++) meta_monitor_manager_clear_output (&old_outputs[i]); g_free (old_outputs); } void meta_monitor_manager_clear_mode (MetaCrtcMode *mode) { g_free (mode->name); if (mode->driver_notify) mode->driver_notify (mode); memset (mode, 0, sizeof (*mode)); } static void meta_monitor_manager_free_mode_array (MetaCrtcMode *old_modes, int n_old_modes) { int i; for (i = 0; i < n_old_modes; i++) meta_monitor_manager_clear_mode (&old_modes[i]); g_free (old_modes); } void meta_monitor_manager_clear_crtc (MetaCrtc *crtc) { if (crtc->driver_notify) crtc->driver_notify (crtc); memset (crtc, 0, sizeof (*crtc)); } static void meta_monitor_manager_free_crtc_array (MetaCrtc *old_crtcs, int n_old_crtcs) { int i; for (i = 0; i < n_old_crtcs; i++) meta_monitor_manager_clear_crtc (&old_crtcs[i]); g_free (old_crtcs); } static void meta_monitor_manager_finalize (GObject *object) { MetaMonitorManager *manager = META_MONITOR_MANAGER (object); meta_monitor_manager_free_output_array (manager->outputs, manager->n_outputs); meta_monitor_manager_free_mode_array (manager->modes, manager->n_modes); meta_monitor_manager_free_crtc_array (manager->crtcs, manager->n_crtcs); g_list_free_full (manager->logical_monitors, g_object_unref); cleanup_pending_cleanup_state (manager); g_signal_handler_disconnect (meta_get_backend (), manager->experimental_features_changed_handler_id); 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->config_manager); g_clear_object (&manager->up_client); 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 char * meta_monitor_manager_real_get_edid_file (MetaMonitorManager *manager, MetaOutput *output) { return NULL; } 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; klass->get_edid_file = meta_monitor_manager_real_get_edid_file; klass->read_edid = meta_monitor_manager_real_read_edid; klass->is_lid_closed = meta_monitor_manager_real_is_lid_closed; 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[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); } 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 * make_display_name (MetaMonitorManager *manager, MetaOutput *output) { g_autofree char *inches = NULL; g_autofree char *vendor_name = NULL; if (meta_output_is_laptop (output)) return g_strdup (_("Built-in display")); if (output->width_mm > 0 && output->height_mm > 0) { double d = sqrt (output->width_mm * output->width_mm + output->height_mm * output->height_mm); inches = diagonal_to_str (d / 25.4); } if (g_strcmp0 (output->vendor, "unknown") != 0) { if (!manager->pnp_ids) manager->pnp_ids = gnome_pnp_ids_new (); vendor_name = gnome_pnp_ids_get_pnp_id (manager->pnp_ids, output->vendor); if (!vendor_name) vendor_name = g_strdup (output->vendor); } else { if (inches != NULL) vendor_name = g_strdup (_("Unknown")); else vendor_name = g_strdup (_("Unknown Display")); } if (inches != NULL) { /* TRANSLATORS: this is a monitor vendor name, followed by a * size in inches, like 'Dell 15"' */ return g_strdup_printf (_("%s %s"), vendor_name, inches); } else { return g_strdup (vendor_name); } } 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 (); } } static gboolean meta_monitor_manager_handle_get_resources (MetaDBusDisplayConfig *skeleton, GDBusMethodInvocation *invocation) { MetaMonitorManager *manager = META_MONITOR_MANAGER (skeleton); MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_GET_CLASS (skeleton); GVariantBuilder crtc_builder, output_builder, mode_builder; unsigned int i, j; int max_screen_width; int max_screen_height; 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 (i = 0; i < manager->n_crtcs; i++) { MetaCrtc *crtc = &manager->crtcs[i]; GVariantBuilder transforms; 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); g_variant_builder_add (&crtc_builder, "(uxiiiiiuaua{sv})", i, /* ID */ (gint64)crtc->crtc_id, (int)crtc->rect.x, (int)crtc->rect.y, (int)crtc->rect.width, (int)crtc->rect.height, (int)(crtc->current_mode ? crtc->current_mode - manager->modes : -1), (guint32)crtc->transform, &transforms, NULL /* properties */); } for (i = 0; i < manager->n_outputs; i++) { MetaOutput *output = &manager->outputs[i]; GVariantBuilder crtcs, modes, clones, properties; GBytes *edid; char *edid_file; g_variant_builder_init (&crtcs, G_VARIANT_TYPE ("au")); for (j = 0; j < output->n_possible_crtcs; j++) g_variant_builder_add (&crtcs, "u", (unsigned)(output->possible_crtcs[j] - manager->crtcs)); g_variant_builder_init (&modes, G_VARIANT_TYPE ("au")); for (j = 0; j < output->n_modes; j++) g_variant_builder_add (&modes, "u", (unsigned)(output->modes[j] - manager->modes)); g_variant_builder_init (&clones, G_VARIANT_TYPE ("au")); for (j = 0; j < output->n_possible_clones; j++) g_variant_builder_add (&clones, "u", (unsigned)(output->possible_clones[j] - manager->outputs)); 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_take_string (make_display_name (manager, output))); 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_file = manager_class->get_edid_file (manager, output); if (edid_file) { g_variant_builder_add (&properties, "{sv}", "edid-file", g_variant_new_take_string (edid_file)); } else { 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)); } g_variant_builder_add (&output_builder, "(uxiausauaua{sv})", i, /* ID */ (gint64)output->winsys_id, (int)(output->crtc ? output->crtc - manager->crtcs : -1), &crtcs, output->name, &modes, &clones, &properties); } for (i = 0; i < manager->n_modes; i++) { MetaCrtcMode *mode = &manager->modes[i]; 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); return TRUE; } 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; 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_source_remove (manager->persistent_timeout_id); manager->persistent_timeout_id = 0; } 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, "[ukwm] 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 = META_MONITOR_MANAGER (skeleton); GVariantBuilder monitors_builder; GVariantBuilder logical_monitors_builder; GVariantBuilder properties_builder; GList *l; int i; MetaMonitorManagerCapability capabilities; int max_screen_width, max_screen_height; 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; MetaOutput *main_output; 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; 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, 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)); main_output = meta_monitor_get_main_output (monitor); display_name = make_display_name (manager, main_output); g_variant_builder_add (&monitor_properties_builder, "{sv}", "display-name", g_variant_new_take_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}")); capabilities = meta_monitor_manager_get_capabilities (manager); if ((capabilities & META_MONITOR_MANAGER_CAPABILITY_MIRRORING) == 0) { g_variant_builder_add (&properties_builder, "{sv}", "supports-mirroring", g_variant_new_boolean (FALSE)); } 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)); } 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 gboolean meta_monitor_manager_is_scale_supported (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode, float 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 (supported_scales[i] == 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 (manager, config->layout_mode, monitor, monitor_mode, scale)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Scale not supported by backend"); return FALSE; } } } return TRUE; } static gboolean meta_monitor_manager_is_config_complete (MetaMonitorManager *manager, MetaMonitorsConfig *config) { GList *l; unsigned int configured_monitor_count = 0; unsigned int expected_monitor_count = 0; 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) configured_monitor_count++; } for (l = manager->monitors; l; l = l->next) { MetaMonitor *monitor = l->data; if (meta_monitor_is_laptop_panel (monitor)) { if (!meta_monitor_manager_is_lid_closed (manager)) expected_monitor_count++; } else { expected_monitor_count++; } } if (configured_monitor_count != expected_monitor_count) 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; 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; } g_variant_lookup (properties_variant, "underscanning", "b", &enable_underscanning); 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: 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 (!meta_verify_logical_monitor_config (logical_monitor_config, layout_mode, manager, 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: 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 = META_MONITOR_MANAGER (skeleton); 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; 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; } logical_monitor_configs = g_list_append (logical_monitor_configs, logical_monitor_config); } 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 = META_MONITOR_MANAGER (skeleton); 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; } if (output_index >= manager->n_outputs) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Invalid output id"); return TRUE; } output = &manager->outputs[output_index]; 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 = META_MONITOR_MANAGER (skeleton); MetaMonitorManagerClass *klass; 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; } if (crtc_id >= manager->n_crtcs) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Invalid crtc id"); return TRUE; } crtc = &manager->crtcs[crtc_id]; 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 = META_MONITOR_MANAGER (skeleton); MetaMonitorManagerClass *klass; 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; } if (crtc_id >= manager->n_crtcs) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Invalid crtc id"); return TRUE; } crtc = &manager->crtcs[crtc_id]; 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 meta_monitor_manager_display_config_init (MetaDBusDisplayConfigIface *iface) { iface->handle_get_resources = meta_monitor_manager_handle_get_resources; iface->handle_change_backlight = meta_monitor_manager_handle_change_backlight; iface->handle_get_crtc_gamma = meta_monitor_manager_handle_get_crtc_gamma; iface->handle_set_crtc_gamma = meta_monitor_manager_handle_set_crtc_gamma; iface->handle_get_current_state = meta_monitor_manager_handle_get_current_state; iface->handle_apply_monitors_config = meta_monitor_manager_handle_apply_monitors_config; } 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), connection, "/org/ukui/ukwm/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.ukui.ukwm.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); } int meta_monitor_manager_get_num_logical_monitors (MetaMonitorManager *manager) { return g_list_length (manager->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; } MetaMonitor * meta_monitor_manager_get_primary_monitor (MetaMonitorManager *manager) { return find_monitor (manager, meta_monitor_is_primary); } 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; } 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 (POINT_IN_RECT (x, y, logical_monitor->rect)) return logical_monitor; } return NULL; } 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, MetaScreenDirection 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; } GList * meta_monitor_manager_get_monitors (MetaMonitorManager *manager) { return manager->monitors; } MetaOutput * meta_monitor_manager_get_outputs (MetaMonitorManager *manager, unsigned int *n_outputs) { *n_outputs = manager->n_outputs; return manager->outputs; } void meta_monitor_manager_get_resources (MetaMonitorManager *manager, MetaCrtcMode **modes, unsigned int *n_modes, MetaCrtc **crtcs, unsigned int *n_crtcs, MetaOutput **outputs, unsigned int *n_outputs) { if (modes) { *modes = manager->modes; *n_modes = manager->n_modes; } if (crtcs) { *crtcs = manager->crtcs; *n_crtcs = manager->n_crtcs; } if (outputs) { *outputs = manager->outputs; *n_outputs = manager->n_outputs; } } void meta_monitor_manager_get_screen_size (MetaMonitorManager *manager, int *width, int *height) { *width = manager->screen_width; *height = manager->screen_height; } static void generate_monitors (MetaMonitorManager *manager) { unsigned int i; for (i = 0; i < manager->n_outputs; i++) { MetaOutput *output = &manager->outputs[i]; if (output->tile_info.group_id) { if (is_main_tiled_monitor_output (output)) { MetaMonitorTiled *monitor_tiled; monitor_tiled = meta_monitor_tiled_new (manager, output); manager->monitors = g_list_append (manager->monitors, monitor_tiled); } } else { MetaMonitorNormal *monitor_normal; monitor_normal = meta_monitor_normal_new (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 cleanup_pending_cleanup_state (MetaMonitorManager *manager) { if (manager->pending_cleanup.monitors) { g_list_free_full (manager->pending_cleanup.monitors, g_object_unref); manager->pending_cleanup.monitors = NULL; } if (manager->pending_cleanup.outputs) { meta_monitor_manager_free_output_array (manager->pending_cleanup.outputs, manager->pending_cleanup.n_outputs); manager->pending_cleanup.outputs = NULL; manager->pending_cleanup.n_outputs = 0; } if (manager->pending_cleanup.modes) { meta_monitor_manager_free_mode_array (manager->pending_cleanup.modes, manager->pending_cleanup.n_modes); manager->pending_cleanup.modes = NULL; manager->pending_cleanup.n_modes = 0; } if (manager->pending_cleanup.crtcs) { meta_monitor_manager_free_crtc_array (manager->pending_cleanup.crtcs, manager->pending_cleanup.n_crtcs); manager->pending_cleanup.crtcs = NULL; manager->pending_cleanup.n_crtcs = 0; } } void meta_monitor_manager_read_current_state (MetaMonitorManager *manager) { MetaOutput *old_outputs; MetaCrtc *old_crtcs; MetaCrtcMode *old_modes; unsigned int n_old_outputs, n_old_crtcs, n_old_modes; /* Some implementations of read_current use the existing information * we have available, so don't free the old configuration until after * read_current finishes. */ old_outputs = manager->outputs; n_old_outputs = manager->n_outputs; old_crtcs = manager->crtcs; n_old_crtcs = manager->n_crtcs; old_modes = manager->modes; n_old_modes = manager->n_modes; manager->serial++; META_MONITOR_MANAGER_GET_CLASS (manager)->read_current (manager); /* * We must delay cleaning up the old hardware state, because the current * logical state, when running on top of X11/Xrandr, may still be based on it * for some time. The logical state may not be updated immediately, in case * it is reconfigured, but after getting the response from a logical state * configuration request to the X server. To be able to handle events and * other things needing the logical state between the request and the * response, the hardware state the logical state points to must be kept * alive. * * If there is already a hardware state pending cleaning up, it means that * the current logical state is still using that hardware state, so we can * destroy the just replaced state immedietaley. */ if (manager->pending_cleanup.outputs || manager->pending_cleanup.crtcs || manager->pending_cleanup.monitors) { if (manager->monitors) { g_list_free_full (manager->monitors, g_object_unref); manager->monitors = NULL; } meta_monitor_manager_free_output_array (old_outputs, n_old_outputs); meta_monitor_manager_free_mode_array (old_modes, n_old_modes); meta_monitor_manager_free_crtc_array (old_crtcs, n_old_crtcs); } else { manager->pending_cleanup.outputs = old_outputs; manager->pending_cleanup.n_outputs = n_old_outputs; manager->pending_cleanup.modes = old_modes; manager->pending_cleanup.n_modes = n_old_modes; manager->pending_cleanup.crtcs = old_crtcs; manager->pending_cleanup.n_crtcs = n_old_crtcs; manager->pending_cleanup.monitors = manager->monitors; manager->monitors = NULL; } generate_monitors (manager); } static void meta_monitor_manager_notify_monitors_changed (MetaMonitorManager *manager) { MetaBackend *backend = meta_get_backend (); manager->current_switch_config = META_MONITOR_SWITCH_CONFIG_UNKNOWN; meta_backend_monitors_changed (backend); g_signal_emit (manager, signals[MONITORS_CHANGED_INTERNAL], 0); g_signal_emit_by_name (manager, "monitors-changed"); } 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; else manager->layout_mode = meta_monitor_manager_get_default_layout_mode (manager); 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); cleanup_pending_cleanup_state (manager); } 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) { manager->layout_mode = META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL; meta_monitor_manager_rebuild_logical_monitors_derived (manager, config); } void meta_monitor_manager_rebuild_derived (MetaMonitorManager *manager, MetaMonitorsConfig *config) { GList *old_logical_monitors; meta_monitor_manager_update_monitor_modes_derived (manager); 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); cleanup_pending_cleanup_state (manager); } 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); /* For UKUI 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, MetaLogicalMonitor *logical_monitor, gfloat matrix[6]) { MetaMonitorTransform transform; gfloat viewport[9]; if (!calculate_viewport_matrix (manager, logical_monitor, viewport)) return FALSE; transform = logical_monitor->transform; multiply_matrix (viewport, transform_matrices[transform], matrix); return TRUE; } /** * meta_monitor_manager_get_monitor_for_output: * @manager: A #MetaMonitorManager * @id: A valid #MetaOutput id * * Returns: The monitor index or -1 if @id isn't valid or the output * isn't associated with a logical monitor. */ gint meta_monitor_manager_get_monitor_for_output (MetaMonitorManager *manager, guint id) { MetaOutput *output; GList *l; g_return_val_if_fail (META_IS_MONITOR_MANAGER (manager), -1); g_return_val_if_fail (id < manager->n_outputs, -1); output = &manager->outputs[id]; if (!output || !output->crtc) return -1; for (l = manager->logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; if (meta_rectangle_contains_rect (&logical_monitor->rect, &output->crtc->rect)) return logical_monitor->number; } return -1; } /** * 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))) { MetaOutput *main_output = meta_monitor_get_main_output (monitor); return main_output->crtc->logical_monitor->number; } } return -1; } 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_monitor_manager_is_lid_closed (manager) && 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; } ukwm/src/backends/meta-input-settings-private.h0000664000175000017500000001716513220600404020567 0ustar fengfeng/* -*- 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 "display-private.h" #include "meta-monitor-manager-private.h" #include #ifdef HAVE_LIBWACOM #include #endif #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, GDesktopDeviceSendEvents 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, GDesktopTouchpadClickMethod mode); void (* set_keyboard_repeat) (MetaInputSettings *settings, gboolean repeat, guint delay, guint interval); void (* set_tablet_mapping) (MetaInputSettings *settings, ClutterInputDevice *device, GDesktopTabletMapping 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, GDesktopPointerAccelProfile profile); void (* set_trackball_accel_profile) (MetaInputSettings *settings, ClutterInputDevice *device, GDesktopPointerAccelProfile 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, GDesktopStylusButtonAction primary, GDesktopStylusButtonAction secondary); gboolean (* has_two_finger_scroll) (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); GDesktopTabletMapping 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); #ifdef HAVE_LIBWACOM WacomDevice * meta_input_settings_get_tablet_wacom_device (MetaInputSettings *settings, ClutterInputDevice *device); #endif gboolean meta_input_device_is_trackball (ClutterInputDevice *device); #endif /* META_INPUT_SETTINGS_PRIVATE_H */ ukwm/src/backends/meta-stage.h0000664000175000017500000000472713233511035015232 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef META_STAGE_H #define META_STAGE_H #include #include "meta-cursor.h" #include G_BEGIN_DECLS #define META_TYPE_STAGE (meta_stage_get_type ()) #define META_STAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_STAGE, MetaStage)) #define META_STAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_STAGE, MetaStageClass)) #define META_IS_STAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_STAGE)) #define META_IS_STAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_STAGE)) #define META_STAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_STAGE, MetaStageClass)) typedef struct _MetaStage MetaStage; typedef struct _MetaStageClass MetaStageClass; typedef struct _MetaOverlay MetaOverlay; struct _MetaStageClass { ClutterStageClass parent_class; }; struct _MetaStage { ClutterStage parent; }; GType meta_stage_get_type (void) G_GNUC_CONST; ClutterActor *meta_stage_new (void); 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, ClutterRect *rect); void meta_stage_set_active (MetaStage *stage, gboolean is_active); void meta_stage_update_view_layout (MetaStage *stage); G_END_DECLS #endif /* META_STAGE_H */ ukwm/src/backends/meta-backend.c0000664000175000017500000007522613233511035015513 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include #include #include #include #include #include "meta-backend-private.h" #include "meta-input-settings-private.h" #include "backends/x11/meta-backend-x11.h" #include "meta-cursor-tracker-private.h" #include "meta-stage.h" #ifdef HAVE_REMOTE_DESKTOP #include "backends/meta-dbus-session-watcher.h" #include "backends/meta-screen-cast.h" #include "backends/meta-remote-desktop.h" #endif #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #endif #include "backends/meta-idle-monitor-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor-manager-dummy.h" #include "backends/meta-settings-private.h" #define META_IDLE_MONITOR_CORE_DEVICE 0 enum { KEYMAP_CHANGED, KEYMAP_LAYOUT_GROUP_CHANGED, LAST_DEVICE_CHANGED, 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; MetaEgl *egl; MetaSettings *settings; #ifdef HAVE_REMOTE_DESKTOP MetaDbusSessionWatcher *dbus_session_watcher; MetaScreenCast *screen_cast; MetaRemoteDesktop *remote_desktop; #endif ClutterBackend *clutter_backend; ClutterActor *stage; gboolean is_pointer_position_initialized; guint device_update_idle_id; GHashTable *device_monitors; int current_device_id; MetaPointerConstraint *client_pointer_constraint; MetaDnd *dnd; }; 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); 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); #endif if (priv->device_update_idle_id) g_source_remove (priv->device_update_idle_id); g_hash_table_destroy (priv->device_monitors); g_clear_object (&priv->settings); 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 center_pointer (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); MetaMonitorManager *monitor_manager = priv->monitor_manager; MetaLogicalMonitor *primary; primary = meta_monitor_manager_get_primary_logical_monitor (monitor_manager); meta_backend_warp_pointer (backend, primary->rect.x + primary->rect.width / 2, primary->rect.y + primary->rect.height / 2); } void meta_backend_monitors_changed (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); ClutterDeviceManager *manager = clutter_device_manager_get_default (); ClutterInputDevice *device = clutter_device_manager_get_core_device (manager, CLUTTER_POINTER_DEVICE); ClutterPoint 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)) { center_pointer (backend); priv->is_pointer_position_initialized = TRUE; } } meta_cursor_renderer_force_update (priv->cursor_renderer); } 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, int device_id) { return META_BACKEND_GET_CLASS (backend)->create_idle_monitor (backend, device_id); } static void create_device_monitor (MetaBackend *backend, int device_id) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); MetaIdleMonitor *idle_monitor; g_assert (g_hash_table_lookup (priv->device_monitors, &device_id) == NULL); idle_monitor = meta_backend_create_idle_monitor (backend, device_id); g_hash_table_insert (priv->device_monitors, &idle_monitor->device_id, idle_monitor); } static void destroy_device_monitor (MetaBackend *backend, int device_id) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); g_hash_table_remove (priv->device_monitors, &device_id); } static void meta_backend_monitor_device (MetaBackend *backend, ClutterInputDevice *device) { int device_id; device_id = clutter_input_device_get_device_id (device); create_device_monitor (backend, device_id); } static void on_device_added (ClutterDeviceManager *device_manager, ClutterInputDevice *device, gpointer user_data) { MetaBackend *backend = META_BACKEND (user_data); int device_id = clutter_input_device_get_device_id (device); create_device_monitor (backend, device_id); } 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 (ClutterDeviceManager *manager) { const GSList *devices; devices = clutter_device_manager_peek_devices (manager); for (; devices; devices = devices->next) { ClutterInputDevice *device = devices->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; return TRUE; } return FALSE; } static inline gboolean check_has_slave_touchscreen (ClutterDeviceManager *manager) { const GSList *devices; devices = clutter_device_manager_peek_devices (manager); for (; devices; devices = devices->next) { ClutterInputDevice *device = devices->data; if (clutter_input_device_get_device_mode (device) != CLUTTER_INPUT_MODE_MASTER && clutter_input_device_get_device_type (device) == CLUTTER_TOUCHSCREEN_DEVICE) return TRUE; } return FALSE; } static void on_device_removed (ClutterDeviceManager *device_manager, ClutterInputDevice *device, gpointer user_data) { MetaBackend *backend = META_BACKEND (user_data); MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); int device_id = clutter_input_device_get_device_id (device); destroy_device_monitor (backend, device_id); /* If the device the user last interacted goes away, check again pointer * visibility. */ if (priv->current_device_id == device_id) { MetaCursorTracker *cursor_tracker = priv->cursor_tracker; gboolean has_touchscreen, has_pointing_device; ClutterInputDeviceType device_type; device_type = clutter_input_device_get_device_type (device); has_touchscreen = check_has_slave_touchscreen (device_manager); 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 (device_manager); meta_cursor_tracker_set_pointer_visible (cursor_tracker, has_pointing_device && !has_touchscreen); } } } static MetaMonitorManager * create_monitor_manager (MetaBackend *backend) { 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); } static void create_device_monitors (MetaBackend *backend, ClutterDeviceManager *device_manager) { const GSList *devices; const GSList *l; create_device_monitor (backend, META_IDLE_MONITOR_CORE_DEVICE); devices = clutter_device_manager_peek_devices (device_manager); for (l = devices; l; l = l->next) { ClutterInputDevice *device = l->data; meta_backend_monitor_device (backend, device); } } static void set_initial_pointer_visibility (MetaBackend *backend, ClutterDeviceManager *device_manager) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); const GSList *devices; const GSList *l; gboolean has_touchscreen = FALSE; devices = clutter_device_manager_peek_devices (device_manager); for (l = devices; l; l = l->next) { ClutterInputDevice *device = l->data; has_touchscreen |= device_is_slave_touchscreen (device); } meta_cursor_tracker_set_pointer_visible (priv->cursor_tracker, !has_touchscreen); } static MetaInputSettings * meta_backend_create_input_settings (MetaBackend *backend) { return META_BACKEND_GET_CLASS (backend)->create_input_settings (backend); } #ifdef HAVE_REMOTE_DESKTOP static gboolean is_screen_cast_enabled (MetaBackend *backend) { MetaSettings *settings = meta_backend_get_settings (backend); return meta_settings_is_experimental_feature_enabled ( settings, META_EXPERIMENTAL_FEATURE_SCREEN_CAST); } static gboolean is_remote_desktop_enabled (MetaBackend *backend) { MetaSettings *settings = meta_backend_get_settings (backend); return meta_settings_is_experimental_feature_enabled ( settings, META_EXPERIMENTAL_FEATURE_REMOTE_DESKTOP); } #endif /* HAVE_REMOTE_DESKTOP */ static void meta_backend_real_post_init (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); ClutterDeviceManager *device_manager = clutter_device_manager_get_default (); priv->stage = meta_stage_new (); clutter_actor_realize (priv->stage); META_BACKEND_GET_CLASS (backend)->select_stage_events (backend); priv->monitor_manager = create_monitor_manager (backend); 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 (g_int_hash, g_int_equal, NULL, (GDestroyNotify) g_object_unref); create_device_monitors (backend, device_manager); g_signal_connect_object (device_manager, "device-added", G_CALLBACK (on_device_added), backend, 0); g_signal_connect_object (device_manager, "device-removed", G_CALLBACK (on_device_removed), backend, 0); set_initial_pointer_visibility (backend, device_manager); priv->input_settings = meta_backend_create_input_settings (backend); #ifdef HAVE_REMOTE_DESKTOP priv->dbus_session_watcher = g_object_new (META_TYPE_DBUS_SESSION_WATCHER, NULL); if (is_screen_cast_enabled (backend)) priv->screen_cast = meta_screen_cast_new (priv->dbus_session_watcher); if (is_remote_desktop_enabled (backend)) priv->remote_desktop = meta_remote_desktop_new (priv->dbus_session_watcher); #endif /* HAVE_REMOTE_DESKTOP */ if (!meta_monitor_manager_is_headless (priv->monitor_manager)) { center_pointer (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_get_relative_motion_deltas (MetaBackend *backend, const ClutterEvent *event, double *dx, double *dy, double *dx_unaccel, double *dy_unaccel) { return FALSE; } static void meta_backend_class_init (MetaBackendClass *klass) { const gchar *ukwm_stage_views; GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_backend_finalize; 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->get_relative_motion_deltas = meta_backend_real_get_relative_motion_deltas; 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, G_TYPE_INT); ukwm_stage_views = g_getenv ("UKWM_STAGE_VIEWS"); stage_views_disabled = g_strcmp0 (ukwm_stage_views, "0") == 0; } static void experimental_features_changed (MetaSettings *settings, MetaExperimentalFeature old_experimental_features, MetaBackend *backend) { #ifdef HAVE_REMOTE_DESKTOP MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); if (is_screen_cast_enabled (backend) && !priv->screen_cast) priv->screen_cast = meta_screen_cast_new (priv->dbus_session_watcher); else if (!is_screen_cast_enabled (backend)) g_clear_object (&priv->screen_cast); if (is_remote_desktop_enabled (backend) && !priv->remote_desktop) priv->remote_desktop = meta_remote_desktop_new (priv->dbus_session_watcher); else if (!is_remote_desktop_enabled (backend)) g_clear_object (&priv->remote_desktop); #endif /* HAVE_REMOTE_DESKTOP */ } 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); g_signal_connect (priv->settings, "experimental-features-changed", G_CALLBACK (experimental_features_changed), backend); priv->egl = g_object_new (META_TYPE_EGL, NULL); priv->renderer = META_BACKEND_GET_CLASS (backend)->create_renderer (backend); if (!priv->renderer) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create MetaRenderer"); return FALSE; } priv->cursor_tracker = g_object_new (META_TYPE_CURSOR_TRACKER, NULL); priv->dnd = g_object_new (META_TYPE_DND, NULL); priv->orientation_manager = g_object_new (META_TYPE_ORIENTATION_MANAGER, NULL); 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; } 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); } /** * meta_backend_get_idle_monitor: (skip) */ MetaIdleMonitor * meta_backend_get_idle_monitor (MetaBackend *backend, int device_id) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return g_hash_table_lookup (priv->device_monitors, &device_id); } /** * 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; } /** * meta_backend_get_egl: (skip) */ MetaEgl * meta_backend_get_egl (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->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_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_warp_pointer: (skip) */ void meta_backend_warp_pointer (MetaBackend *backend, int x, int y) { META_BACKEND_GET_CLASS (backend)->warp_pointer (backend, x, y); } 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; } static gboolean update_last_device (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); MetaCursorTracker *cursor_tracker = priv->cursor_tracker; ClutterInputDeviceType device_type; ClutterDeviceManager *manager; ClutterInputDevice *device; priv->device_update_idle_id = 0; manager = clutter_device_manager_get_default (); device = clutter_device_manager_get_device (manager, priv->current_device_id); device_type = clutter_input_device_get_device_type (device); g_signal_emit (backend, signals[LAST_DEVICE_CHANGED], 0, priv->current_device_id); 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, int device_id) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); ClutterDeviceManager *manager; ClutterInputDevice *device; if (priv->current_device_id == device_id) return; manager = clutter_device_manager_get_default (); device = clutter_device_manager_get_device (manager, device_id); if (!device || clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER) return; priv->current_device_id = device_id; 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, "[ukwm] update_last_device"); } } gboolean meta_backend_get_relative_motion_deltas (MetaBackend *backend, const ClutterEvent *event, double *dx, double *dy, double *dx_unaccel, double *dy_unaccel) { MetaBackendClass *klass = META_BACKEND_GET_CLASS (backend); return klass->get_relative_motion_deltas (backend, event, dx, dy, dx_unaccel, dy_unaccel); } MetaPointerConstraint * meta_backend_get_client_pointer_constraint (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->client_pointer_constraint; } 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); } /* Ukwm 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 event_prepare (GSource *source, gint *timeout_) { *timeout_ = -1; return clutter_events_pending (); } static gboolean event_check (GSource *source) { return clutter_events_pending (); } static gboolean event_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 event_funcs = { event_prepare, event_check, event_dispatch }; 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; } static ClutterBackend * meta_get_clutter_backend (void) { MetaBackend *backend = meta_get_backend (); return meta_backend_get_clutter_backend (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_clutter_init: (skip) */ void meta_clutter_init (void) { GSource *source; clutter_set_custom_backend_func (meta_get_clutter_backend); if (clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS) { g_warning ("Unable to initialize Clutter.\n"); exit (1); } source = g_source_new (&event_funcs, sizeof (GSource)); g_source_attach (source, NULL); g_source_unref (source); meta_backend_post_init (_backend); } 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); } ukwm/src/backends/meta-screen-cast-stream-src.h0000664000175000017500000000377713233511035020420 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * */ #ifndef META_SCREEN_CAST_STREAM_SRC_H #define META_SCREEN_CAST_STREAM_SRC_H #include #include "clutter/clutter.h" typedef struct _MetaScreenCastStream MetaScreenCastStream; #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); void (* record_frame) (MetaScreenCastStreamSrc *src, uint8_t *data); }; void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src); MetaScreenCastStream * meta_screen_cast_stream_src_get_stream (MetaScreenCastStreamSrc *src); #endif /* META_SCREEN_CAST_STREAM_SRC_H */ ukwm/src/backends/meta-monitor-manager-dummy.c0000664000175000017500000005044313220600404020341 0ustar fengfeng/* -*- 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. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include "config.h" #include "meta-monitor-manager-dummy.h" #include #include #include "backends/meta-backend-private.h" #include "backends/meta-monitor.h" #include "backends/meta-monitor-config-manager.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); static void meta_output_dummy_notify_destroy (MetaOutput *output); #define array_last(a, t) \ g_array_index (a, t, a->len - 1) static void append_monitor (GArray *modes, GArray *crtcs, GArray *outputs, float scale) { MetaCrtcMode modes_decl[] = { { .width = 800, .height = 600, .refresh_rate = 60.0 }, { .width = 1024, .height = 768, .refresh_rate = 60.0 } }; MetaCrtc crtc; MetaOutputDummy *output_dummy; MetaOutput output; unsigned int i; for (i = 0; i < G_N_ELEMENTS (modes_decl); i++) modes_decl[i].mode_id = modes->len + i; g_array_append_vals (modes, modes_decl, G_N_ELEMENTS (modes_decl)); crtc = (MetaCrtc) { .crtc_id = crtcs->len + 1, .all_transforms = ALL_TRANSFORMS, }; g_array_append_val (crtcs, crtc); output_dummy = g_new0 (MetaOutputDummy, 1); *output_dummy = (MetaOutputDummy) { .scale = scale }; output = (MetaOutput) { .winsys_id = outputs->len + 1, .name = g_strdup_printf ("LVDS%d", outputs->len + 1), .vendor = g_strdup ("MetaProducts Inc."), .product = g_strdup ("MetaMonitor"), .serial = g_strdup_printf ("0xC0FFEE-%d", outputs->len + 1), .suggested_x = -1, .suggested_y = -1, .width_mm = 222, .height_mm = 125, .subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN, .preferred_mode = &array_last (modes, MetaCrtcMode), .n_possible_clones = 0, .backlight = -1, .connector_type = META_CONNECTOR_TYPE_LVDS, .driver_private = output_dummy, .driver_notify = (GDestroyNotify) meta_output_dummy_notify_destroy }; output.modes = g_new0 (MetaCrtcMode *, G_N_ELEMENTS (modes_decl)); for (i = 0; i < G_N_ELEMENTS (modes_decl); i++) output.modes[i] = &g_array_index (modes, MetaCrtcMode, modes->len - (i + 1)); output.n_modes = G_N_ELEMENTS (modes_decl); output.possible_crtcs = g_new0 (MetaCrtc *, 1); output.possible_crtcs[0] = &array_last (crtcs, MetaCrtc); output.n_possible_crtcs = 1; g_array_append_val (outputs, output); } static void append_tiled_monitor (GArray *modes, GArray *crtcs, GArray *outputs, int scale) { MetaCrtcMode modes_decl[] = { { .width = 800, .height = 600, .refresh_rate = 60.0 }, { .width = 512, .height = 768, .refresh_rate = 60.0 } }; MetaCrtc crtcs_decl[] = { { .all_transforms = ALL_TRANSFORMS, }, { .all_transforms = ALL_TRANSFORMS, }, }; MetaOutput output; unsigned int i; uint32_t tile_group_id; for (i = 0; i < G_N_ELEMENTS (modes_decl); i++) modes_decl[i].mode_id = modes->len + i; g_array_append_vals (modes, modes_decl, G_N_ELEMENTS (modes_decl)); for (i = 0; i < G_N_ELEMENTS (crtcs_decl); i++) crtcs_decl[i].crtc_id = crtcs->len + i + 1; g_array_append_vals (crtcs, crtcs_decl, G_N_ELEMENTS (crtcs_decl)); tile_group_id = outputs->len + 1; for (i = 0; i < G_N_ELEMENTS (crtcs_decl); i++) { MetaOutputDummy *output_dummy; MetaCrtcMode *preferred_mode; unsigned int j; output_dummy = g_new0 (MetaOutputDummy, 1); *output_dummy = (MetaOutputDummy) { .scale = scale }; preferred_mode = &array_last (modes, MetaCrtcMode), output = (MetaOutput) { .winsys_id = outputs->len + 1, .name = g_strdup_printf ("LVDS%d", outputs->len + 1), .vendor = g_strdup ("MetaProducts Inc."), .product = g_strdup ("MetaMonitor"), .serial = g_strdup_printf ("0xC0FFEE-%d", outputs->len + 1), .suggested_x = -1, .suggested_y = -1, .width_mm = 222, .height_mm = 125, .subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN, .preferred_mode = preferred_mode, .n_possible_clones = 0, .backlight = -1, .connector_type = META_CONNECTOR_TYPE_LVDS, .tile_info = (MetaTileInfo) { .group_id = tile_group_id, .max_h_tiles = G_N_ELEMENTS (crtcs_decl), .max_v_tiles = 1, .loc_h_tile = i, .loc_v_tile = 0, .tile_w = preferred_mode->width, .tile_h = preferred_mode->height }, .driver_private = output_dummy, .driver_notify = (GDestroyNotify) meta_output_dummy_notify_destroy }; output.modes = g_new0 (MetaCrtcMode *, G_N_ELEMENTS (modes_decl)); for (j = 0; j < G_N_ELEMENTS (modes_decl); j++) output.modes[j] = &g_array_index (modes, MetaCrtcMode, modes->len - (j + 1)); output.n_modes = G_N_ELEMENTS (modes_decl); output.possible_crtcs = g_new0 (MetaCrtc *, G_N_ELEMENTS (crtcs_decl)); for (j = 0; j < G_N_ELEMENTS (crtcs_decl); j++) output.possible_crtcs[j] = &g_array_index (crtcs, MetaCrtc, crtcs->len - (j + 1)); output.n_possible_crtcs = G_N_ELEMENTS (crtcs_decl); g_array_append_val (outputs, output); } } static void meta_output_dummy_notify_destroy (MetaOutput *output) { g_clear_pointer (&output->driver_private, g_free); } static void meta_monitor_manager_dummy_read_current (MetaMonitorManager *manager) { unsigned int num_monitors = 1; float *monitor_scales = NULL; const char *num_monitors_str; const char *monitor_scales_str; const char *tiled_monitors_str; gboolean tiled_monitors; unsigned int i; GArray *outputs; GArray *crtcs; GArray *modes; /* To control what monitor configuration is generated, there are two available * environmental variables that can be used: * * UKWM_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. * * UKWM_DEBUG_DUMMY_MONITOR_SCALES * * A comma separated list that specifies the scales of the dummy monitors. * * UKWM_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. * * UKWM_DEBUG_NUM_DUMMY_MONITORS=2 * UKWM_DEBUG_DUMMY_MONITOR_SCALES=1,2 * UKWM_DEBUG_TILED_DUMMY_MONITORS=1 */ num_monitors_str = getenv ("UKWM_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 ("UKWM_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_str = g_getenv ("UKWM_DEBUG_TILED_DUMMY_MONITORS"); tiled_monitors = g_strcmp0 (tiled_monitors_str, "1") == 0; modes = g_array_sized_new (FALSE, TRUE, sizeof (MetaCrtcMode), MAX_MODES); crtcs = g_array_sized_new (FALSE, TRUE, sizeof (MetaCrtc), MAX_CRTCS); outputs = g_array_sized_new (FALSE, TRUE, sizeof (MetaOutput), MAX_OUTPUTS); for (i = 0; i < num_monitors; i++) { if (tiled_monitors) append_tiled_monitor (modes, crtcs, outputs, monitor_scales[i]); else append_monitor (modes, crtcs, outputs, monitor_scales[i]); } manager->modes = (MetaCrtcMode *) modes->data; manager->n_modes = modes->len; manager->crtcs = (MetaCrtc *) crtcs->data; manager->n_crtcs = crtcs->len; manager->outputs = (MetaOutput *) outputs->data; manager->n_outputs = outputs->len; g_array_free (modes, FALSE); g_array_free (crtcs, FALSE); g_array_free (outputs, FALSE); } 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) { 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) { crtc->rect.x = 0; crtc->rect.y = 0; crtc->rect.width = 0; crtc->rect.height = 0; crtc->current_mode = NULL; } else { MetaCrtcMode *mode; MetaOutput *output; unsigned int j; int width, height; mode = crtc_info->mode; if (meta_monitor_transform_is_rotated (crtc_info->transform)) { width = mode->height; height = mode->width; } else { width = mode->width; height = mode->height; } crtc->rect.x = crtc_info->x; crtc->rect.y = crtc_info->y; crtc->rect.width = width; crtc->rect.height = height; crtc->current_mode = mode; crtc->transform = crtc_info->transform; for (j = 0; j < crtc_info->outputs->len; j++) { output = ((MetaOutput**)crtc_info->outputs->pdata)[j]; output->is_dirty = TRUE; output->crtc = 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 (i = 0; i < manager->n_crtcs; i++) { MetaCrtc *crtc = &manager->crtcs[i]; crtc->logical_monitor = NULL; if (crtc->is_dirty) { crtc->is_dirty = FALSE; continue; } crtc->rect.x = 0; crtc->rect.y = 0; crtc->rect.width = 0; crtc->rect.height = 0; crtc->current_mode = NULL; } /* Disable outputs not mentioned in the list */ for (i = 0; i < manager->n_outputs; i++) { MetaOutput *output = &manager->outputs[i]; if (output->is_dirty) { output->is_dirty = FALSE; continue; } output->crtc = NULL; 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, 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: 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; capabilities |= META_MONITOR_MANAGER_CAPABILITY_MIRRORING; 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_class_init (MetaMonitorManagerDummyClass *klass) { MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_CLASS (klass); manager_class->read_current = meta_monitor_manager_dummy_read_current; 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) { const char *nested_offscreen_transform; nested_offscreen_transform = g_getenv ("UKWM_DEBUG_NESTED_OFFSCREEN_TRANSFORM"); if (g_strcmp0 (nested_offscreen_transform, "1") == 0) manager->is_transform_handled = FALSE; else manager->is_transform_handled = TRUE; } ukwm/src/backends/meta-pointer-constraint.c0000664000175000017500000000400213233511035017746 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #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) { } 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); } ukwm/src/backends/meta-barrier.c0000664000175000017500000002316313220600404015536 0ustar fengfeng/* -*- 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 #include #include #include "backends/native/meta-backend-native.h" #include "backends/native/meta-barrier-native.h" #include "backends/x11/meta-backend-x11.h" #include "backends/x11/meta-barrier-x11.h" #include G_DEFINE_TYPE (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); if (meta_barrier_is_active (barrier)) { meta_bug ("MetaBarrier %p was destroyed while it was still active.", barrier); } 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 defined(HAVE_XI23) if (META_IS_BACKEND_X11 (meta_get_backend ()) && !meta_is_wayland_compositor ()) priv->impl = meta_barrier_impl_x11_new (barrier); #endif 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); g_type_class_add_private (object_class, sizeof(MetaBarrierPrivate)); } void meta_barrier_destroy (MetaBarrier *barrier) { MetaBarrierImpl *impl = barrier->priv->impl; if (impl) return META_BARRIER_IMPL_GET_CLASS (impl)->destroy (impl); g_object_unref (barrier); } static void meta_barrier_init (MetaBarrier *barrier) { barrier->priv = G_TYPE_INSTANCE_GET_PRIVATE (barrier, META_TYPE_BARRIER, MetaBarrierPrivate); } 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) ukwm/src/backends/edid-parse.c0000664000175000017500000003244513220600404015204 0ustar fengfeng/* * 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 "edid.h" #include #include #include #include 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; } } ukwm/src/backends/meta-dbus-session-watcher.h0000664000175000017500000000352213233511035020170 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 */ ukwm/src/backends/meta-monitor-config-manager.c0000664000175000017500000014303113233511035020454 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "config.h" #include "backends/meta-monitor-config-manager.h" #include "backends/meta-monitor-config-migration.h" #include "backends/meta-monitor-config-store.h" #include "backends/meta-monitor-manager-private.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_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) { unsigned int i; for (i = 0; i < output->n_possible_crtcs; i++) { MetaCrtc *crtc = output->possible_crtcs[i]; if (is_crtc_assigned (crtc, crtc_infos)) continue; return crtc; } return NULL; } typedef struct { MetaMonitorManager *monitor_manager; MetaLogicalMonitorConfig *logical_monitor_config; MetaMonitorConfig *monitor_config; GPtrArray *crtc_infos; GPtrArray *output_infos; } 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; int crtc_x, crtc_y; 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); 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; if (meta_monitor_manager_is_transform_handled (data->monitor_manager, crtc, transform)) crtc_transform = transform; else crtc_transform = META_MONITOR_TRANSFORM_NORMAL; meta_monitor_calculate_crtc_pos (monitor, mode, output, crtc_transform, &crtc_x, &crtc_y); crtc_info = g_slice_new0 (MetaCrtcInfo); *crtc_info = (MetaCrtcInfo) { .crtc = crtc, .mode = monitor_crtc_mode->crtc_mode, .x = crtc_x, .y = crtc_y, .transform = crtc_transform, .outputs = g_ptr_array_new () }; g_ptr_array_add (crtc_info->outputs, output); /* * Currently, MetaCrtcInfo are deliberately offset incorrectly to carry over * logical monitor location inside the MetaCrtc struct, when in fact this * depends on the framebuffer configuration. This will eventually be negated * when setting the actual KMS mode. * * TODO: Remove this hack when we don't need to rely on MetaCrtc to pass * logical monitor state. */ crtc_info->x += data->logical_monitor_config->layout.x; crtc_info->y += data->logical_monitor_config->layout.y; /* * 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, MetaLogicalMonitorConfig *logical_monitor_config, MetaMonitorConfig *monitor_config, GPtrArray *crtc_infos, GPtrArray *output_infos, 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, .logical_monitor_config = logical_monitor_config, .monitor_config = monitor_config, .crtc_infos = crtc_infos, .output_infos = output_infos }; 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, MetaLogicalMonitorConfig *logical_monitor_config, GPtrArray *crtc_infos, GPtrArray *output_infos, 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, logical_monitor_config, monitor_config, crtc_infos, output_infos, 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; 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); for (l = config->logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; if (!assign_logical_monitor_crtcs (manager, logical_monitor_config, crtc_infos, output_infos, error)) { g_ptr_array_free (crtc_infos, TRUE); g_ptr_array_free (output_infos, TRUE); return FALSE; } } *out_crtc_infos = crtc_infos; *out_output_infos = output_infos; return TRUE; } static MetaMonitorsConfigKey * create_key_for_current_state (MetaMonitorManager *monitor_manager) { MetaMonitorsConfigKey *config_key; GList *l; GList *monitor_specs; 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) && meta_monitor_manager_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) 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 = create_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) } 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 (meta_monitor_manager_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_memdup (mode_spec, sizeof (MetaMonitorModeSpec)), .enable_underscanning = meta_monitor_is_underscanning (monitor) }; return monitor_config; } static MetaLogicalMonitorConfig * create_preferred_logical_monitor_config (MetaMonitorManager *monitor_manager, MetaMonitor *monitor, int x, int y, MetaLogicalMonitorConfig *primary_logical_monitor_config, MetaLogicalMonitorLayoutMode layout_mode) { MetaMonitorMode *mode; int width, height; float scale; 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, mode); switch (layout_mode) { case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: width /= scale; height /= scale; break; case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL: break; } monitor_config = create_monitor_config (monitor, mode); logical_monitor_config = g_new0 (MetaLogicalMonitorConfig, 1); *logical_monitor_config = (MetaLogicalMonitorConfig) { .layout = (MetaRectangle) { .x = x, .y = y, .width = width, .height = height }, .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; 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); primary_logical_monitor_config = create_preferred_logical_monitor_config (monitor_manager, primary_monitor, 0, 0, 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) && meta_monitor_manager_is_lid_closed (monitor_manager)) continue; logical_monitor_config = create_preferred_logical_monitor_config (monitor_manager, monitor, x, 0, 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; primary_monitor = find_primary_monitor (monitor_manager); if (!primary_monitor) return NULL; layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); primary_logical_monitor_config = create_preferred_logical_monitor_config (monitor_manager, primary_monitor, 0, 0, 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; 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); primary_logical_monitor_config = create_preferred_logical_monitor_config (monitor_manager, primary_monitor, x, y, 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, 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); } 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 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; MetaLogicalMonitorLayoutMode layout_mode; MetaMonitorConfig *monitor_config; MetaMonitorConfig *current_monitor_config; if (!meta_monitor_manager_get_is_builtin_display_on (config_manager->monitor_manager)) return NULL; if (!config_manager->current_config) return NULL; if (g_list_length (config_manager->current_config->logical_monitor_configs) != 1) return NULL; current_logical_monitor_config = config_manager->current_config->logical_monitor_configs->data; if (rotate) transform = (current_logical_monitor_config->transform + 1) % META_MONITOR_TRANSFORM_FLIPPED; if (current_logical_monitor_config->transform == transform) return NULL; if (g_list_length (current_logical_monitor_config->monitor_configs) != 1) return NULL; current_monitor_config = current_logical_monitor_config->monitor_configs->data; monitor_config = g_new0 (MetaMonitorConfig, 1); *monitor_config = (MetaMonitorConfig) { .monitor_spec = meta_monitor_spec_clone (current_monitor_config->monitor_spec), .mode_spec = g_memdup (current_monitor_config->mode_spec, sizeof (MetaMonitorModeSpec)), .enable_underscanning = current_monitor_config->enable_underscanning }; logical_monitor_config = g_memdup (current_logical_monitor_config, sizeof (MetaLogicalMonitorConfig)); logical_monitor_config->monitor_configs = g_list_append (NULL, monitor_config); 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; } logical_monitor_configs = g_list_append (NULL, logical_monitor_config); 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); } 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, 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; MetaLogicalMonitorLayoutMode layout_mode; GList *monitors; GList *l; layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); 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, 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; monitor = meta_monitor_manager_get_laptop_panel (monitor_manager); if (!monitor) return NULL; layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); primary_logical_monitor_config = create_preferred_logical_monitor_config (monitor_manager, monitor, 0, 0, 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; if (!meta_monitor_manager_can_switch_config (monitor_manager)) return NULL; switch (config_type) { case META_MONITOR_SWITCH_CONFIG_ALL_MIRROR: return create_for_switch_config_all_mirror (config_manager); case META_MONITOR_SWITCH_CONFIG_ALL_LINEAR: return meta_monitor_config_manager_create_linear (config_manager); case META_MONITOR_SWITCH_CONFIG_EXTERNAL: return create_for_switch_config_external (config_manager); case META_MONITOR_SWITCH_CONFIG_BUILTIN: return create_for_switch_config_builtin (config_manager); case META_MONITOR_SWITCH_CONFIG_UNKNOWN: g_warn_if_reached (); break; } return NULL; } 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) { 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; } 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; 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; 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, 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_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_adjecent_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_adjecent_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_adjecent_neighbour (config, logical_monitor_config)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Logical monitors not adjecent"); 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; } ukwm/src/backends/meta-idle-monitor-private.h0000664000175000017500000000406213220600404020164 0ustar fengfeng/* -*- 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 #include "display-private.h" #include #include typedef struct { MetaIdleMonitor *monitor; guint id; MetaIdleMonitorWatchFunc callback; gpointer user_data; GDestroyNotify notify; guint64 timeout_msec; int idle_source_id; } MetaIdleMonitorWatch; struct _MetaIdleMonitor { GObject parent_instance; GHashTable *watches; int device_id; }; struct _MetaIdleMonitorClass { GObjectClass parent_class; gint64 (*get_idletime) (MetaIdleMonitor *monitor); MetaIdleMonitorWatch * (*make_watch) (MetaIdleMonitor *monitor, guint64 timeout_msec, MetaIdleMonitorWatchFunc callback, gpointer user_data, GDestroyNotify notify); }; void _meta_idle_monitor_watch_fire (MetaIdleMonitorWatch *watch); #endif /* META_IDLE_MONITOR_PRIVATE_H */ ukwm/src/backends/meta-monitor-config-migration.c0000664000175000017500000011155413220600404021033 0ustar fengfeng/* -*- 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; 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->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) && 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 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 = -1.0, }; *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, GError **error) { g_autofree char *backup_path = NULL; g_autoptr (GFile) backup_file = NULL; g_autofree char *user_file_path = NULL; g_autoptr (GFile) user_file = NULL; user_file_path = g_build_filename (g_get_user_config_dir (), "monitors.xml", 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; 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; float scale; 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; } scale = meta_monitor_calculate_mode_scale (monitor, monitor_mode); logical_monitor_config->scale = scale; } config->layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); 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; } ukwm/src/backends/meta-settings.c0000664000175000017500000002616613233511035015763 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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" enum { UI_SCALING_FACTOR_CHANGED, GLOBAL_SCALING_FACTOR_CHANGED, FONT_DPI_CHANGED, EXPERIMENTAL_FEATURES_CHANGED, N_SIGNALS }; static guint signals[N_SIGNALS]; struct _MetaSettings { GObject parent; MetaBackend *backend; GSettings *interface_settings; GSettings *ukwm_settings; int ui_scaling_factor; int global_scaling_factor; int font_dpi; MetaExperimentalFeature experimental_features; gboolean experimental_features_overridden; }; 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); 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); } 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); } 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; } void meta_settings_enable_experimental_feature (MetaSettings *settings, MetaExperimentalFeature feature) { g_assert (settings->experimental_features_overridden); settings->experimental_features |= feature; } static gboolean experimental_features_handler (GVariant *features_variant, gpointer *result, gpointer data) { MetaSettings *settings = data; GVariantIter features_iter; char *feature; 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)) { /* So far no experimental features defined. */ if (g_str_equal (feature, "scale-monitor-framebuffer")) features |= META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER; else if (g_str_equal (feature, "screen-cast")) features |= META_EXPERIMENTAL_FEATURE_SCREEN_CAST; else if (g_str_equal (feature, "remote-desktop")) features |= META_EXPERIMENTAL_FEATURE_REMOTE_DESKTOP; else g_info ("Unknown experimental feature '%s'\n", feature); } if (features != settings->experimental_features) { settings->experimental_features = features; *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->ukwm_settings, "experimental-features", experimental_features_handler, settings)); } static void ukwm_settings_changed (GSettings *ukwm_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); } 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->ukwm_settings); g_clear_object (&settings->interface_settings); G_OBJECT_CLASS (meta_settings_parent_class)->dispose (object); } static void meta_settings_init (MetaSettings *settings) { settings->interface_settings = g_settings_new ("org.gnome.desktop.interface"); g_signal_connect (settings->interface_settings, "changed", G_CALLBACK (interface_settings_changed), settings); settings->ukwm_settings = g_settings_new ("org.ukui.ukwm"); g_signal_connect (settings->ukwm_settings, "changed", G_CALLBACK (ukwm_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); } 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[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); } ukwm/src/backends/meta-egl.h0000664000175000017500000002154213233511035014670 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #ifndef META_EGL_H #define META_EGL_H #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_egl_has_extensions (MetaEgl *egl, EGLDisplay display, char ***missing_extensions, char *first_extension, ...); gboolean meta_egl_initialize (MetaEgl *egl, EGLDisplay display, GError **error); gboolean meta_egl_choose_config (MetaEgl *egl, EGLDisplay display, const EGLint *attrib_list, EGLConfig *chosen_config, 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); EGLSurface meta_egl_create_pbuffer_surface (MetaEgl *egl, EGLDisplay display, EGLConfig config, const EGLint *attrib_list, GError **error); EGLDisplay meta_egl_get_platform_display (MetaEgl *egl, EGLenum platform, void *native_display, const EGLint *attrib_list, 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, char ***missing_extensions, 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 */ ukwm/src/backends/meta-monitor.h0000664000175000017500000002103413233511035015604 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef META_MONITOR_H #define META_MONITOR_H #include #include "backends/meta-monitor-manager-private.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), } 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) MetaMonitorTiled * meta_monitor_tiled_new (MetaMonitorManager *monitor_manager, MetaOutput *main_output); MetaMonitorNormal * meta_monitor_normal_new (MetaMonitorManager *monitor_manager, MetaOutput *output); MetaMonitorSpec * meta_monitor_get_spec (MetaMonitor *monitor); gboolean meta_monitor_is_active (MetaMonitor *monitor); MetaOutput * meta_monitor_get_main_output (MetaMonitor *monitor); gboolean meta_monitor_is_primary (MetaMonitor *monitor); gboolean meta_monitor_supports_underscanning (MetaMonitor *monitor); gboolean meta_monitor_is_underscanning (MetaMonitor *monitor); gboolean meta_monitor_is_laptop_panel (MetaMonitor *monitor); gboolean meta_monitor_is_same_as (MetaMonitor *monitor, MetaMonitor *other_monitor); GList * meta_monitor_get_outputs (MetaMonitor *monitor); void meta_monitor_get_current_resolution (MetaMonitor *monitor, int *width, int *height); void meta_monitor_derive_layout (MetaMonitor *monitor, MetaRectangle *layout); void meta_monitor_get_physical_dimensions (MetaMonitor *monitor, int *width_mm, int *height_mm); CoglSubpixelOrder meta_monitor_get_subpixel_order (MetaMonitor *monitor); const char * meta_monitor_get_connector (MetaMonitor *monitor); const char * meta_monitor_get_vendor (MetaMonitor *monitor); const char * meta_monitor_get_product (MetaMonitor *monitor); const char * meta_monitor_get_serial (MetaMonitor *monitor); MetaConnectorType meta_monitor_get_connector_type (MetaMonitor *monitor); uint32_t meta_monitor_tiled_get_tile_group_id (MetaMonitorTiled *monitor_tiled); gboolean meta_monitor_get_suggested_position (MetaMonitor *monitor, int *x, int *y); MetaLogicalMonitor * meta_monitor_get_logical_monitor (MetaMonitor *monitor); MetaMonitorMode * meta_monitor_get_mode_from_id (MetaMonitor *monitor, const char *monitor_mode_id); MetaMonitorMode * meta_monitor_get_mode_from_spec (MetaMonitor *monitor, MetaMonitorModeSpec *monitor_mode_spec); MetaMonitorMode * meta_monitor_get_preferred_mode (MetaMonitor *monitor); MetaMonitorMode * meta_monitor_get_current_mode (MetaMonitor *monitor); void meta_monitor_derive_current_mode (MetaMonitor *monitor); void meta_monitor_set_current_mode (MetaMonitor *monitor, MetaMonitorMode *mode); GList * meta_monitor_get_modes (MetaMonitor *monitor); void meta_monitor_calculate_crtc_pos (MetaMonitor *monitor, MetaMonitorMode *monitor_mode, MetaOutput *output, MetaMonitorTransform crtc_transform, int *out_x, int *out_y); float meta_monitor_calculate_mode_scale (MetaMonitor *monitor, MetaMonitorMode *monitor_mode); float * meta_monitor_calculate_supported_scales (MetaMonitor *monitor, MetaMonitorMode *monitor_mode, MetaMonitorScalesConstraint constraints, int *n_supported_scales); const char * meta_monitor_mode_get_id (MetaMonitorMode *monitor_mode); MetaMonitorModeSpec * meta_monitor_mode_get_spec (MetaMonitorMode *monitor_mode); void meta_monitor_mode_get_resolution (MetaMonitorMode *monitor_mode, int *width, int *height); float meta_monitor_mode_get_refresh_rate (MetaMonitorMode *monitor_mode); MetaCrtcModeFlag meta_monitor_mode_get_flags (MetaMonitorMode *monitor_mode); gboolean meta_monitor_mode_foreach_crtc (MetaMonitor *monitor, MetaMonitorMode *mode, MetaMonitorModeFunc func, gpointer user_data, GError **error); gboolean meta_monitor_mode_foreach_output (MetaMonitor *monitor, MetaMonitorMode *mode, MetaMonitorModeFunc func, gpointer user_data, GError **error); MetaMonitorSpec * meta_monitor_spec_clone (MetaMonitorSpec *monitor_id); gboolean meta_monitor_spec_equals (MetaMonitorSpec *monitor_id, MetaMonitorSpec *other_monitor_id); int meta_monitor_spec_compare (MetaMonitorSpec *monitor_spec_a, MetaMonitorSpec *monitor_spec_b); void meta_monitor_spec_free (MetaMonitorSpec *monitor_id); #endif /* META_MONITOR_H */ ukwm/src/backends/meta-renderer.c0000664000175000017500000000634513233511035015726 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #include "config.h" #include #include "backends/meta-backend-private.h" #include "backends/meta-renderer.h" typedef struct _MetaRendererPrivate { GList *views; } MetaRendererPrivate; G_DEFINE_TYPE_WITH_PRIVATE (MetaRenderer, meta_renderer, G_TYPE_OBJECT) 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) { return META_RENDERER_GET_CLASS (renderer)->create_view (renderer, logical_monitor); } void meta_renderer_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; MetaRendererView *view; view = meta_renderer_create_view (renderer, logical_monitor); priv->views = g_list_append (priv->views, view); } } void meta_renderer_set_legacy_view (MetaRenderer *renderer, MetaRendererView *legacy_view) { MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer); g_assert (!priv->views); priv->views = g_list_append (priv->views, legacy_view); } GList * meta_renderer_get_views (MetaRenderer *renderer) { MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer); return priv->views; } 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; } static void meta_renderer_init (MetaRenderer *renderer) { } static void meta_renderer_class_init (MetaRendererClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_renderer_finalize; } ukwm/src/backends/meta-screen-cast-session.h0000664000175000017500000000404613233511035020011 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * */ #ifndef META_SCREEN_CAST_SESSION_H #define META_SCREEN_CAST_SESSION_H #include "backends/meta-screen-cast.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) char * meta_screen_cast_session_get_object_path (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); #endif /* META_SCREEN_CAST_SESSION_H */ ukwm/src/backends/meta-screen-cast-stream-src.c0000664000175000017500000005365013233511035020406 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015-2017 Red Hat Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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-stream.h" #include "clutter/clutter-ukwm.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)) enum { PROP_0, PROP_STREAM, }; enum { READY, CLOSED, N_SIGNALS }; static guint signals[N_SIGNALS]; typedef struct _MetaSpaType { uint32_t format; uint32_t props; struct spa_type_meta meta; struct spa_type_data data; struct spa_type_media_type media_type; struct spa_type_media_subtype media_subtype; struct spa_type_format_video format_video; struct spa_type_video_format video_format; } MetaSpaType; typedef struct _MetaPipeWireSource { GSource base; struct pw_loop *pipewire_loop; } MetaPipeWireSource; typedef struct _MetaScreenCastStreamSrcPrivate { MetaScreenCastStream *stream; struct pw_core *pipewire_core; struct pw_remote *pipewire_remote; struct pw_type *pipewire_type; MetaPipeWireSource *pipewire_source; struct spa_hook pipewire_remote_listener; gboolean is_enabled; struct pw_stream *pipewire_stream; struct spa_hook pipewire_stream_listener; MetaSpaType spa_type; uint8_t params_buffer[1024]; struct spa_video_info_raw video_format; uint64_t last_frame_timestamp_us; } 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)) #define PROP(f, key, type, ...) \ SPA_POD_PROP (f, key, 0, type, 1, __VA_ARGS__) #define PROP_U_MM(f, key, type, ...) \ SPA_POD_PROP (f, key, (SPA_POD_PROP_FLAG_UNSET | \ SPA_POD_PROP_RANGE_MIN_MAX), \ type, 3, __VA_ARGS__) 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 void meta_screen_cast_stream_src_record_frame (MetaScreenCastStreamSrc *src, uint8_t *data) { MetaScreenCastStreamSrcClass *klass = META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); klass->record_frame (src, data); } void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) { MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); uint32_t buffer_id; struct spa_buffer *buffer; uint8_t *map = NULL; uint8_t *data; uint64_t now_us; now_us = g_get_monotonic_time (); if (priv->last_frame_timestamp_us != 0 && (now_us - priv->last_frame_timestamp_us < ((1000000 * priv->video_format.max_framerate.denom) / priv->video_format.max_framerate.num))) return; if (!priv->pipewire_stream) return; buffer_id = pw_stream_get_empty_buffer (priv->pipewire_stream); if (buffer_id == SPA_ID_INVALID) return; buffer = pw_stream_peek_buffer (priv->pipewire_stream, buffer_id); if (buffer->datas[0].type == priv->spa_type.data.MemFd) { map = mmap (NULL, buffer->datas[0].maxsize + buffer->datas[0].mapoffset, PROT_READ | PROT_WRITE, MAP_SHARED, buffer->datas[0].fd, 0); if (map == MAP_FAILED) { g_warning ("Failed to mmap pipewire stream buffer: %s\n", strerror (errno)); return; } data = SPA_MEMBER (map, buffer->datas[0].mapoffset, uint8_t); } else if (buffer->datas[0].type == priv->spa_type.data.MemPtr) { data = buffer->datas[0].data; } else { return; } meta_screen_cast_stream_src_record_frame (src, data); priv->last_frame_timestamp_us = now_us; if (map) munmap (map, buffer->datas[0].maxsize + buffer->datas[0].mapoffset); pw_stream_send_buffer (priv->pipewire_stream, buffer_id); } 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); priv->is_enabled = FALSE; } static void meta_screen_cast_stream_src_notify_closed (MetaScreenCastStreamSrc *src) { g_signal_emit (src, signals[CLOSED], 0); } 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); uint32_t node_id; switch (state) { case PW_STREAM_STATE_ERROR: g_warning ("pipewire stream error: %s", error_message); meta_screen_cast_stream_src_notify_closed (src); break; case PW_STREAM_STATE_CONFIGURE: node_id = pw_stream_get_node_id (priv->pipewire_stream); g_signal_emit (src, signals[READY], 0, (unsigned int) node_id); break; case PW_STREAM_STATE_UNCONNECTED: case PW_STREAM_STATE_CONNECTING: case PW_STREAM_STATE_READY: case PW_STREAM_STATE_PAUSED: 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; } } static void on_stream_format_changed (void *data, struct spa_format *format) { MetaScreenCastStreamSrc *src = data; MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); struct pw_type *pipewire_type = priv->pipewire_type; struct spa_type_param_alloc_buffers *param_alloc_buffers; struct spa_pod_builder pod_builder = { NULL }; struct spa_pod_frame object_frame; struct spa_pod_frame prop_frame; struct spa_param *params[1]; const int bpp = 4; if (!format) { pw_stream_finish_format (priv->pipewire_stream, SPA_RESULT_OK, NULL, 0); return; } spa_format_video_raw_parse (format, &priv->video_format, &priv->spa_type.format_video); spa_pod_builder_init (&pod_builder, priv->params_buffer, sizeof (priv->params_buffer)); param_alloc_buffers = &pipewire_type->param_alloc_buffers; spa_pod_builder_object (&pod_builder, &object_frame, 0, param_alloc_buffers->Buffers, PROP (&prop_frame, param_alloc_buffers->size, SPA_POD_TYPE_INT, (priv->video_format.size.width * priv->video_format.size.height * bpp)), PROP (&prop_frame, param_alloc_buffers->stride, SPA_POD_TYPE_INT, priv->video_format.size.width * bpp), PROP_U_MM (&prop_frame, param_alloc_buffers->buffers, SPA_POD_TYPE_INT, 16, 2, 16), PROP (&prop_frame, param_alloc_buffers->align, SPA_POD_TYPE_INT, 16)); params[0] = SPA_POD_BUILDER_DEREF (&pod_builder, object_frame.ref, struct spa_param); pw_stream_finish_format (priv->pipewire_stream, SPA_RESULT_OK, params, G_N_ELEMENTS (params)); } static const struct pw_stream_events stream_events = { PW_VERSION_STREAM_EVENTS, .state_changed = on_stream_state_changed, .format_changed = on_stream_format_changed, }; 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; const struct spa_format *format; uint8_t buffer[1024]; struct spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT (buffer, sizeof (buffer)); struct spa_pod_frame format_frame; struct spa_pod_frame prop_frame; MetaSpaType *spa_type = &priv->spa_type; int width, height; float frame_rate; MetaFraction frame_rate_fraction; pipewire_stream = pw_stream_new (priv->pipewire_remote, "meta-screen-cast-src", NULL); meta_screen_cast_stream_src_get_specs (src, &width, &height, &frame_rate); frame_rate_fraction = meta_fraction_from_double (frame_rate); spa_pod_builder_format (&pod_builder, &format_frame, spa_type->format, spa_type->media_type.video, spa_type->media_subtype.raw, PROP (&prop_frame, spa_type->format_video.format, SPA_POD_TYPE_ID, spa_type->video_format.BGRx), PROP (&prop_frame, spa_type->format_video.size, SPA_POD_TYPE_RECTANGLE, width, height), PROP (&prop_frame, spa_type->format_video.framerate, SPA_POD_TYPE_FRACTION, 0, 1), PROP_U_MM (&prop_frame, spa_type->format_video.max_framerate, SPA_POD_TYPE_FRACTION, frame_rate_fraction.num, frame_rate_fraction.denom, 1, 1, frame_rate_fraction.num, frame_rate_fraction.denom)); format = SPA_POD_BUILDER_DEREF (&pod_builder, format_frame.ref, struct spa_format); pw_stream_add_listener (pipewire_stream, &priv->pipewire_stream_listener, &stream_events, src); if (!pw_stream_connect (pipewire_stream, PW_DIRECTION_OUTPUT, PW_STREAM_MODE_BUFFER, NULL, PW_STREAM_FLAG_NONE, 1, &format)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Could not connect"); return NULL; } return pipewire_stream; } static void on_state_changed (void *data, enum pw_remote_state old, enum pw_remote_state state, const char *error_message) { MetaScreenCastStreamSrc *src = data; MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); struct pw_stream *pipewire_stream; GError *error = NULL; switch (state) { case PW_REMOTE_STATE_ERROR: g_warning ("pipewire remote error: %s\n", error_message); meta_screen_cast_stream_src_notify_closed (src); break; case PW_REMOTE_STATE_CONNECTED: pipewire_stream = create_pipewire_stream (src, &error); if (!pipewire_stream) { g_warning ("Could not create pipewire stream: %s", error->message); g_error_free (error); meta_screen_cast_stream_src_notify_closed (src); } else { priv->pipewire_stream = pipewire_stream; } break; case PW_REMOTE_STATE_UNCONNECTED: case PW_REMOTE_STATE_CONNECTING: break; } } 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; int result; result = pw_loop_iterate (pipewire_source->pipewire_loop, 0); if (result == SPA_RESULT_ERRNO) g_warning ("pipewire_loop_iterate failed: %s", strerror (errno)); else if (result != SPA_RESULT_OK) g_warning ("pipewire_loop_iterate failed: %d", result); 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 void init_spa_type (MetaSpaType *type, struct spa_type_map *map) { type->format = spa_type_map_get_id (map, SPA_TYPE__Format); type->props = spa_type_map_get_id (map, SPA_TYPE__Props); spa_type_meta_map (map, &type->meta); spa_type_data_map (map, &type->data); spa_type_media_type_map (map, &type->media_type); spa_type_media_subtype_map (map, &type->media_subtype); spa_type_format_video_map (map, &type->format_video); spa_type_video_format_map (map, &type->video_format); } static MetaPipeWireSource * create_pipewire_source (void) { MetaPipeWireSource *pipewire_source; pipewire_source = (MetaPipeWireSource *) g_source_new (&pipewire_source_funcs, sizeof (MetaPipeWireSource)); pipewire_source->pipewire_loop = pw_loop_new (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_remote_events remote_events = { PW_VERSION_REMOTE_EVENTS, .state_changed = on_state_changed, }; 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 (); priv->pipewire_core = pw_core_new (priv->pipewire_source->pipewire_loop, NULL); if (!priv->pipewire_core) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create pipewire core"); return FALSE; } #if PIPEWIRE_VERSION_MICRO == 4 priv->pipewire_remote = pw_remote_new (priv->pipewire_core, NULL); #elif PIPEWIRE_VERSION_MICRO >= 5 priv->pipewire_remote = pw_remote_new (priv->pipewire_core, NULL, 0); #else priv->pipewire_remote = NULL; #endif if (!priv->pipewire_remote) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Couldn't creat pipewire remote"); return FALSE; } pw_remote_add_listener (priv->pipewire_remote, &priv->pipewire_remote_listener, &remote_events, src); priv->pipewire_type = pw_core_get_type (priv->pipewire_core); init_spa_type (&priv->spa_type, priv->pipewire_type->map); if (pw_remote_connect (priv->pipewire_remote) != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Couldn't connect pipewire remote"); 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, (GDestroyNotify) pw_stream_destroy); g_clear_pointer (&priv->pipewire_remote, (GDestroyNotify) pw_remote_destroy); g_clear_pointer (&priv->pipewire_core, (GDestroyNotify) pw_core_destroy); g_source_destroy (&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) { } 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); } ukwm/src/backends/meta-display-config-shared.h0000664000175000017500000000251113220600404020263 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2013 Red Hat Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ /* This file is shared between ukwm (src/core/meta-display-config-shared.h) and gnome-desktop (libgnome-desktop/meta-xrandr-shared.h). The canonical place for all changes is ukwm. 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 */ ukwm/src/backends/meta-stage.c0000664000175000017500000002050513233511035015215 0ustar fengfeng/* * Copyright (C) 2014 Red Hat * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #include #include "meta-stage.h" #include #include #include #include "backends/meta-backend-private.h" #include "clutter/clutter-ukwm.h" struct _MetaOverlay { gboolean enabled; CoglPipeline *pipeline; CoglTexture *texture; ClutterRect current_rect; ClutterRect previous_rect; gboolean previous_is_valid; }; struct _MetaStagePrivate { GList *overlays; gboolean is_active; }; typedef struct _MetaStagePrivate MetaStagePrivate; G_DEFINE_TYPE_WITH_PRIVATE (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, ClutterRect *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) { if (!overlay->enabled) return; g_assert (meta_is_wayland_compositor ()); cogl_framebuffer_draw_rectangle (cogl_get_draw_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)); overlay->previous_rect = overlay->current_rect; overlay->previous_is_valid = TRUE; } static void meta_stage_finalize (GObject *object) { MetaStage *stage = META_STAGE (object); MetaStagePrivate *priv = meta_stage_get_instance_private (stage); GList *l = priv->overlays; while (l) { meta_overlay_free (l->data); l = g_list_delete_link (l, l); } G_OBJECT_CLASS (meta_stage_parent_class)->finalize (object); } static void meta_stage_paint (ClutterActor *actor) { MetaStage *stage = META_STAGE (actor); MetaStagePrivate *priv = meta_stage_get_instance_private (stage); GList *l; CLUTTER_ACTOR_CLASS (meta_stage_parent_class)->paint (actor); for (l = priv->overlays; l; l = l->next) meta_overlay_paint (l->data); } static void meta_stage_activate (ClutterStage *actor) { MetaStage *stage = META_STAGE (actor); MetaStagePrivate *priv = meta_stage_get_instance_private (stage); CLUTTER_STAGE_CLASS (meta_stage_parent_class)->activate (actor); priv->is_active = TRUE; } static void meta_stage_deactivate (ClutterStage *actor) { MetaStage *stage = META_STAGE (actor); MetaStagePrivate *priv = meta_stage_get_instance_private (stage); CLUTTER_STAGE_CLASS (meta_stage_parent_class)->deactivate (actor); priv->is_active = FALSE; } 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; } static void meta_stage_init (MetaStage *stage) { clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), FALSE); } ClutterActor * meta_stage_new (void) { return g_object_new (META_TYPE_STAGE, "cursor-visible", FALSE, NULL); } static void queue_redraw_for_overlay (MetaStage *stage, MetaOverlay *overlay) { cairo_rectangle_int_t clip; /* Clear the location the overlay was at before, if we need to. */ if (overlay->previous_is_valid) { clip.x = floorf (overlay->previous_rect.origin.x), clip.y = floorf (overlay->previous_rect.origin.y), clip.width = ceilf (overlay->previous_rect.size.width), clip.height = ceilf (overlay->previous_rect.size.height), clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stage), &clip); overlay->previous_is_valid = FALSE; } /* Draw the overlay at the new position */ if (overlay->enabled) { clip.x = floorf (overlay->current_rect.origin.x), clip.y = floorf (overlay->current_rect.origin.y), clip.width = ceilf (overlay->current_rect.size.width), clip.height = ceilf (overlay->current_rect.size.height), clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stage), &clip); } } MetaOverlay * meta_stage_create_cursor_overlay (MetaStage *stage) { MetaStagePrivate *priv = meta_stage_get_instance_private (stage); MetaOverlay *overlay; overlay = meta_overlay_new (); priv->overlays = g_list_prepend (priv->overlays, overlay); return overlay; } void meta_stage_remove_cursor_overlay (MetaStage *stage, MetaOverlay *overlay) { MetaStagePrivate *priv = meta_stage_get_instance_private (stage); GList *link; link = g_list_find (priv->overlays, overlay); if (!link) return; priv->overlays = g_list_delete_link (priv->overlays, link); meta_overlay_free (overlay); } void meta_stage_update_cursor_overlay (MetaStage *stage, MetaOverlay *overlay, CoglTexture *texture, ClutterRect *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) { MetaStagePrivate *priv = meta_stage_get_instance_private (stage); ClutterEvent event = { 0 }; /* 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 (priv->is_active == is_active) return; event.type = 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); } ukwm/src/backends/meta-screen-cast.h0000664000175000017500000000260513233511035016327 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * */ #ifndef META_SCREEN_CAST_H #define META_SCREEN_CAST_H #include #include "backends/meta-dbus-session-watcher.h" #include "meta-dbus-screen-cast.h" #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); MetaScreenCast * meta_screen_cast_new (MetaDbusSessionWatcher *session_watcher); #endif /* META_SCREEN_CAST_H */ ukwm/src/backends/meta-dnd-private.h0000664000175000017500000000230513220600404016325 0ustar fengfeng/* -*- 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 gboolean meta_dnd_handle_xdnd_event (MetaBackend *backend, MetaCompositor *compositor, MetaDisplay *display, XEvent *xev); #ifdef HAVE_WAYLAND void meta_dnd_wayland_handle_begin_modal (MetaCompositor *compositor); #endif #endif /* META_DND_PRIVATE_H */ ukwm/src/backends/meta-input-settings.c0000664000175000017500000022657113260055411017123 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2014 Red Hat, Inc. * Copyright 2017 Tianjin KYLIN Information Technology Co., 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, see . * * Author: Carlos Garnacho */ /** * SECTION:input-settings * @title: MetaInputSettings * @short_description: Ukwm input device configuration */ #include "config.h" #include #include "meta-backend-private.h" #include "meta-input-settings-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor.h" #include #include static GQuark quark_tool_settings = 0; typedef struct _MetaInputSettingsPrivate MetaInputSettingsPrivate; typedef struct _DeviceMappingInfo DeviceMappingInfo; struct _DeviceMappingInfo { MetaInputSettings *input_settings; ClutterInputDevice *device; GSettings *settings; guint changed_id; #ifdef HAVE_LIBWACOM WacomDevice *wacom_device; #endif guint *group_modes; }; struct _MetaInputSettingsPrivate { ClutterDeviceManager *device_manager; MetaMonitorManager *monitor_manager; guint monitors_changed_id; GSettings *mouse_settings; GSettings *touchpad_settings; GSettings *trackball_settings; GSettings *keyboard_settings; GSettings *gsd_settings; GHashTable *mappable_devices; ClutterVirtualInputDevice *virtual_pad_keyboard; #ifdef HAVE_LIBWACOM WacomDeviceDatabase *wacom_db; #endif GHashTable *two_finger_devices; /* Pad ring/strip emission */ struct { ClutterInputDevice *pad; MetaPadActionType action; guint number; gdouble value; } last_pad_action_info; }; 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; const GSList *devices; GSList *list = NULL; priv = meta_input_settings_get_instance_private (settings); devices = clutter_device_manager_peek_devices (priv->device_manager); while (devices) { ClutterInputDevice *device = devices->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); devices = devices->next; } 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->gsd_settings); g_clear_pointer (&priv->mappable_devices, g_hash_table_unref); if (priv->monitors_changed_id && priv->monitor_manager) { g_signal_handler_disconnect (priv->monitor_manager, priv->monitors_changed_id); priv->monitors_changed_id = 0; } g_clear_object (&priv->monitor_manager); #ifdef HAVE_LIBWACOM if (priv->wacom_db) libwacom_database_destroy (priv->wacom_db); #endif 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, ConfigBoolFunc func, gboolean enabled) { GSList *devices, *d; devices = meta_input_settings_get_devices (input_settings, type); for (d = devices; d; d = d->next) settings_device_set_bool_setting (input_settings, d->data, func, enabled); 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; GDesktopTouchpadHandedness 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 G_DESKTOP_TOUCHPAD_HANDEDNESS_RIGHT: enabled = FALSE; break; case G_DESKTOP_TOUCHPAD_HANDEDNESS_LEFT: enabled = TRUE; break; case G_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, 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 { GDesktopTouchpadHandedness touchpad_handedness; settings_set_bool_setting (input_settings, CLUTTER_POINTER_DEVICE, 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 == G_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, GDesktopPointerAccelProfile 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) { GDesktopPointerAccelProfile 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); const GSList *devices; const GSList *l; devices = clutter_device_manager_peek_devices (priv->device_manager); 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); } } } 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_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, 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, 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, input_settings_class->set_disable_while_typing, enabled); } } 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) { 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, 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) { 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, 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, (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, (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; GDesktopTouchpadClickMethod 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; GDesktopDeviceSendEvents 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); } } gboolean meta_input_device_is_trackball (ClutterInputDevice *device) { gboolean is_trackball; char *name; if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER) return FALSE; name = g_ascii_strdown (clutter_input_device_get_device_name (device), -1); is_trackball = strstr (name, "trackball") != NULL; g_free (name); return is_trackball; } static void update_trackball_scroll_button (MetaInputSettings *input_settings, ClutterInputDevice *device) { MetaInputSettingsClass *input_settings_class; MetaInputSettingsPrivate *priv; guint button; if (device && !meta_input_device_is_trackball (device)) return; priv = meta_input_settings_get_instance_private (input_settings); input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); /* 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) { const GSList *devices; devices = clutter_device_manager_peek_devices (priv->device_manager); while (devices) { device = devices->data; if (meta_input_device_is_trackball (device)) input_settings_class->set_scroll_button (input_settings, device, button); devices = devices->next; } } } 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 gboolean logical_monitor_has_monitor (MetaMonitorManager *monitor_manager, 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 TRUE; } return FALSE; } static MetaLogicalMonitor * meta_input_settings_find_logical_monitor (MetaInputSettings *input_settings, GSettings *settings, ClutterInputDevice *device) { MetaInputSettingsPrivate *priv; MetaMonitorManager *monitor_manager; MetaLogicalMonitor *ret = NULL; 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, "display"); 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; if (logical_monitor_has_monitor (monitor_manager, logical_monitor, edid[0], edid[1], edid[2])) { ret = logical_monitor; break; } } out: g_strfreev (edid); return ret; } 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_settings_get_tablet_wacom_device (input_settings, 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"); logical_monitor = meta_input_settings_find_logical_monitor (input_settings, settings, device); } 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 }; MetaLogicalMonitor *logical_monitor; 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) logical_monitor = meta_input_settings_find_logical_monitor (input_settings, settings, device); else logical_monitor = NULL; if (logical_monitor) meta_monitor_manager_get_monitor_matrix (priv->monitor_manager, logical_monitor, matrix); 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; GDesktopTabletMapping 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_settings_get_tablet_wacom_device (input_settings, 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_settings_get_tablet_wacom_device (input_settings, 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_settings_get_tablet_wacom_device (input_settings, 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 (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 (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 (settings == priv->keyboard_settings) { if (strcmp (key, "repeat") == 0 || strcmp (key, "repeat-interval") == 0 || strcmp (key, "delay") == 0) update_keyboard_repeat (input_settings); } } static void mapped_device_changed_cb (GSettings *settings, const gchar *key, DeviceMappingInfo *info) { if (strcmp (key, "display") == 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); } } 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.gnome.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.gnome.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/gnome/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); if (serial == 0) { path = g_strdup_printf ("/org/gnome/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/gnome/desktop/peripherals/stylus/%lx/", serial); } tool_settings = g_settings_new_with_path ("org.gnome.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/gnome/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.gnome.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 device_mapping_info_free (DeviceMappingInfo *info) { #ifdef HAVE_LIBWACOM if (info->wacom_device) libwacom_destroy (info->wacom_device); #endif g_signal_handler_disconnect (info->settings, info->changed_id); 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 ("UKWM_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; #ifdef HAVE_LIBWACOM if (device_type == CLUTTER_TABLET_DEVICE || device_type == CLUTTER_PAD_DEVICE) { WacomError *error = libwacom_error_new (); info->wacom_device = libwacom_new_from_path (priv->wacom_db, clutter_input_device_get_device_node (device), WFALLBACK_NONE, error); if (!info->wacom_device) { g_warning ("Could not get tablet information for '%s': %s", clutter_input_device_get_device_name (device), libwacom_error_get_message (error)); } libwacom_error_free (&error); } #endif 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); } 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; GDesktopStylusButtonAction primary, secondary; 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"); input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); input_settings_class->set_stylus_button_map (input_settings, device, tool, primary, secondary); } 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 (ClutterDeviceManager *device_manager, 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 (ClutterDeviceManager *device_manager, ClutterInputDevice *device, MetaInputSettings *input_settings) { MetaInputSettingsPrivate *priv; priv = meta_input_settings_get_instance_private (input_settings); g_hash_table_remove (priv->mappable_devices, 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 meta_input_settings_tool_changed (ClutterDeviceManager *device_manager, ClutterInputDevice *device, ClutterInputDeviceTool *tool, MetaInputSettings *input_settings) { if (!tool) return; apply_stylus_settings (input_settings, device, tool); } static void check_mappable_devices (MetaInputSettings *input_settings) { MetaInputSettingsPrivate *priv; const GSList *devices, *l; priv = meta_input_settings_get_instance_private (input_settings); devices = clutter_device_manager_peek_devices (priv->device_manager); 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); } } static void meta_input_settings_constructed (GObject *object) { MetaInputSettings *input_settings = META_INPUT_SETTINGS (object); 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_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->device_manager = clutter_device_manager_get_default (); g_signal_connect (priv->device_manager, "device-added", G_CALLBACK (meta_input_settings_device_added), settings); g_signal_connect (priv->device_manager, "device-removed", G_CALLBACK (meta_input_settings_device_removed), settings); g_signal_connect (priv->device_manager, "tool-changed", G_CALLBACK (meta_input_settings_tool_changed), settings); priv->mouse_settings = g_settings_new ("org.gnome.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.gnome.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.gnome.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.gnome.desktop.peripherals.keyboard"); g_signal_connect (priv->keyboard_settings, "changed", G_CALLBACK (meta_input_settings_changed_cb), settings); priv->gsd_settings = g_settings_new ("org.mate.peripherals-mouse"); g_settings_bind (priv->gsd_settings, "double-click", clutter_settings_get_default(), "double-click-time", G_SETTINGS_BIND_GET); priv->mappable_devices = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) device_mapping_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); #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 priv->two_finger_devices = g_hash_table_new (NULL, NULL); } 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; } MetaLogicalMonitor * meta_input_settings_get_tablet_logical_monitor (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); if (!info) return NULL; return meta_input_settings_find_logical_monitor (settings, info->settings, device); } GDesktopTabletMapping meta_input_settings_get_tablet_mapping (MetaInputSettings *settings, ClutterInputDevice *device) { MetaInputSettingsPrivate *priv; DeviceMappingInfo *info; g_return_val_if_fail (META_IS_INPUT_SETTINGS (settings), G_DESKTOP_TABLET_MAPPING_ABSOLUTE); g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), G_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, G_DESKTOP_TABLET_MAPPING_ABSOLUTE); return g_settings_get_enum (info->settings, "mapping"); } static GDesktopPadButtonAction meta_input_settings_get_pad_button_action (MetaInputSettings *input_settings, ClutterInputDevice *pad, guint button) { GDesktopPadButtonAction action; GSettings *settings; g_return_val_if_fail (META_IS_INPUT_SETTINGS (input_settings), G_DESKTOP_PAD_BUTTON_ACTION_NONE); g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (pad), G_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; } #ifdef HAVE_LIBWACOM WacomDevice * meta_input_settings_get_tablet_wacom_device (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); g_return_val_if_fail (info != NULL, NULL); return info->wacom_device; } #endif /* HAVE_LIBWACOM */ 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; const gchar *edid[4] = { 0 }, *pretty_name = NULL; 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 if (info->wacom_device) { /* Output rotation only makes sense on external tablets */ if (libwacom_get_integration_flags (info->wacom_device) != WACOM_DEVICE_INTEGRATED_NONE) return; pretty_name = libwacom_get_name (info->wacom_device); } #endif logical_monitor = meta_input_settings_find_logical_monitor (input_settings, info->settings, device); 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, "display", 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) { ClutterDeviceManager *manager = clutter_device_manager_get_default (); priv->virtual_pad_keyboard = clutter_device_manager_create_virtual_device (manager, 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) != G_DESKTOP_PAD_BUTTON_ACTION_NONE); } static gboolean meta_input_settings_handle_pad_button (MetaInputSettings *input_settings, ClutterInputDevice *pad, const ClutterPadButtonEvent *event) { GDesktopPadButtonAction 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; priv = meta_input_settings_get_instance_private (input_settings); info = g_hash_table_lookup (priv->mappable_devices, pad); #ifdef HAVE_LIBWACOM if (info && info->wacom_device) pretty_name = libwacom_get_name (info->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 G_DESKTOP_PAD_BUTTON_ACTION_SWITCH_MONITOR: if (is_press) meta_input_settings_cycle_tablet_output (input_settings, pad); return TRUE; case G_DESKTOP_PAD_BUTTON_ACTION_HELP: if (is_press) meta_display_request_pad_osd (meta_get_display (), pad, FALSE); return TRUE; case G_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 G_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) { GDesktopPadButtonAction 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 G_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 G_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 G_DESKTOP_PAD_BUTTON_ACTION_HELP: return g_strdup (_("Show on-screen help")); case G_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; } ukwm/src/backends/meta-dbus-session-watcher.c0000664000175000017500000001517613233511035020173 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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); } 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; } ukwm/src/backends/meta-screen-cast-monitor-stream.c0000664000175000017500000002101113233511035021270 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 (GDBusConnection *connection, MetaMonitorManager *monitor_manager, MetaMonitor *monitor, ClutterStage *stage, GError **error) { 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, "connection", connection, "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_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; 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)); } ukwm/src/backends/meta-monitor.c0000664000175000017500000013453513233511035015612 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "config.h" #include "backends/meta-monitor.h" #include "backends/meta-backend-private.h" #include "backends/meta-monitor-manager-private.h" #include "backends/meta-settings-private.h" #define SCALE_FACTORS_PER_INTEGER 4 #define MINIMUM_SCALE_FACTOR 1.0f #define MAXIMUM_SCALE_FACTOR 4.0f #define MINIMUM_LOGICAL_WIDTH 800 #define MINIMUM_LOGICAL_HEIGHT 600 #define MAXIMUM_REFRESH_RATE_DIFF 0.001 typedef struct _MetaMonitorMode { char *id; MetaMonitorModeSpec spec; MetaMonitorCrtcMode *crtc_modes; } MetaMonitorMode; typedef struct _MetaMonitorModeTiled { MetaMonitorMode parent; gboolean is_tiled; } MetaMonitorModeTiled; typedef struct _MetaMonitorPrivate { MetaMonitorManager *monitor_manager; GList *outputs; GList *modes; GHashTable *mode_ids; MetaMonitorMode *preferred_mode; MetaMonitorMode *current_mode; MetaMonitorSpec *spec; /* * 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). */ long winsys_id; } 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; 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; } 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) { MetaOutput *output; output = meta_monitor_get_main_output (monitor); return output->crtc && output->crtc->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; } 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); g_clear_pointer (&priv->outputs, g_list_free); meta_monitor_spec_free (priv->spec); 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->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 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]; MetaMonitorMode *mode; gboolean replace; mode = g_new0 (MetaMonitorMode, 1); mode->spec = (MetaMonitorModeSpec) { .width = crtc_mode->width, .height = crtc_mode->height, .refresh_rate = crtc_mode->refresh_rate, .flags = crtc_mode->flags & HANDLED_CRTC_MODE_FLAGS }, 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; if (output->crtc && crtc_mode == output->crtc->current_mode) monitor_priv->current_mode = mode; } } MetaMonitorNormal * meta_monitor_normal_new (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->monitor_manager = monitor_manager; monitor_priv->outputs = g_list_append (NULL, output); monitor_priv->winsys_id = output->winsys_id; meta_monitor_generate_spec (monitor); meta_monitor_normal_generate_modes (monitor_normal); 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; output = meta_monitor_get_main_output (monitor); *layout = (MetaRectangle) { .x = output->crtc->rect.x, .y = output->crtc->rect.y, .width = output->crtc->rect.width, .height = output->crtc->rect.height }; } 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; *x = output->suggested_x; *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 (MetaMonitorManager *monitor_manager, MetaMonitorTiled *monitor_tiled) { MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (META_MONITOR (monitor_tiled)); unsigned int i; for (i = 0; i < monitor_manager->n_outputs; i++) { MetaOutput *output = &monitor_manager->outputs[i]; 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, 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]; if (monitor_crtc_mode->crtc_mode && (!output->crtc || output->crtc->current_mode != monitor_crtc_mode->crtc_mode)) return FALSE; else if (!monitor_crtc_mode->crtc_mode && output->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.spec = (MetaMonitorModeSpec) { .width = width, .height = height, .refresh_rate = reference_crtc_mode->refresh_rate, .flags = reference_crtc_mode->flags & HANDLED_CRTC_MODE_FLAGS }; 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.spec = (MetaMonitorModeSpec) { .width = crtc_mode->width, .height = crtc_mode->height, .refresh_rate = crtc_mode->refresh_rate, .flags = crtc_mode->flags & HANDLED_CRTC_MODE_FLAGS }; 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 (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->monitor_manager = monitor_manager; 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 (monitor_manager, monitor_tiled); monitor_tiled->main_output = find_untiled_output (monitor_tiled); meta_monitor_generate_spec (monitor); meta_monitor_manager_tiled_monitor_added (monitor_manager, META_MONITOR (monitor_tiled)); meta_monitor_tiled_generate_modes (monitor_tiled); 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_derived_derive_layout (MetaMonitor *monitor, MetaRectangle *layout) { MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (monitor); GList *l; int min_x, min_y, max_x, max_y; min_x = INT_MAX; min_y = INT_MAX; max_x = 0; max_y = 0; for (l = monitor_priv->outputs; l; l = l->next) { MetaOutput *output = l->data; if (!output->crtc) continue; min_x = MIN (output->crtc->rect.x, min_x); min_y = MIN (output->crtc->rect.y, min_y); max_x = MAX (output->crtc->rect.x + output->crtc->rect.width, max_x); max_y = MAX (output->crtc->rect.y + output->crtc->rect.height, max_y); } *layout = (MetaRectangle) { .x = min_x, .y = min_y, .width = max_x - min_x, .height = 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) { MetaMonitor *monitor = META_MONITOR (object); MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (monitor); meta_monitor_manager_tiled_monitor_removed (monitor_priv->monitor_manager, monitor); 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_derived_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) { MetaOutput *output = meta_monitor_get_main_output (monitor); if (output->crtc) return output->crtc->logical_monitor; else return NULL; } 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; } 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; } 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) { 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 ((width_mm == 160 && height_mm == 90) || (width_mm == 160 && height_mm == 100) || (width_mm == 16 && height_mm == 9) || (width_mm == 16 && height_mm == 10)) 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) { 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); } static float get_closest_scale_factor_for_resolution (float width, float height, float scale, float scale_step) { unsigned int i, j; float scaled_h; float scaled_w; float best_scale; int base_scaled_w; gboolean limit_exceeded; gboolean found_one; best_scale = 0; scaled_w = width / scale; scaled_h = height / scale; if (scale < MINIMUM_SCALE_FACTOR || scale > MAXIMUM_SCALE_FACTOR || floorf (scaled_w) < MINIMUM_LOGICAL_WIDTH || floorf (scaled_h) < MINIMUM_LOGICAL_HEIGHT) goto out; if (floorf (scaled_w) == scaled_w && floorf (scaled_h) == scaled_h) return scale; i = 0; found_one = FALSE; limit_exceeded = FALSE; base_scaled_w = floorf (scaled_w); 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_step || current_scale <= scale - scale_step || current_scale < MINIMUM_SCALE_FACTOR || current_scale > MAXIMUM_SCALE_FACTOR) { limit_exceeded = TRUE; continue; } 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 && !limit_exceeded); 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; float scale_steps; GArray *supported_scales; scale_steps = 1.0 / (float) SCALE_FACTORS_PER_INTEGER; 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_steps; if ((constraints & META_MONITOR_SCALES_CONSTRAINT_NO_FRAC) && fmodf (scale_value, 1.0) != 0.0) { continue; } scale = get_closest_scale_factor_for_resolution (width, height, scale_value, scale_steps); 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; } ukwm/src/backends/meta-monitor-config-store.c0000664000175000017500000013447513233511035020212 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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; } 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, 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_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; 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); 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, error)) return; if (!meta_verify_logical_monitor_config (logical_monitor_config, layout_mode, store->monitor_manager, error)) return; } if (parser->current_was_migrated) config_flags |= META_MONITORS_CONFIG_FLAG_MIGRATED; 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, 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 }; 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) { g_string_append_printf (buffer, "%s\n", indentation); g_string_append_printf (buffer, "%s %s\n", indentation, monitor_spec->connector); g_string_append_printf (buffer, "%s %s\n", indentation, monitor_spec->vendor); g_string_append_printf (buffer, "%s %s\n", indentation, monitor_spec->product); g_string_append_printf (buffer, "%s %s\n", indentation, monitor_spec->serial); 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; 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); } void meta_monitor_config_store_add (MetaMonitorConfigStore *config_store, MetaMonitorsConfig *config) { g_hash_table_replace (config_store->configs, config->key, g_object_ref (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); 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, 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); char *user_file_path; GError *error = NULL; user_file_path = g_build_filename (g_get_user_config_dir (), "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, &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, &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); } ukwm/src/backends/meta-barrier-private.h0000664000175000017500000000511713233511035017217 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre * Jonas Ã…dahl */ #ifndef META_BARRIER_PRIVATE_H #define META_BARRIER_PRIVATE_H #include "core/meta-border.h" G_BEGIN_DECLS #define META_TYPE_BARRIER_IMPL (meta_barrier_impl_get_type ()) #define META_BARRIER_IMPL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BARRIER_IMPL, MetaBarrierImpl)) #define META_BARRIER_IMPL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BARRIER_IMPL, MetaBarrierImplClass)) #define META_IS_BARRIER_IMPL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BARRIER_IMPL)) #define META_IS_BARRIER_IMPL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BARRIER_IMPL)) #define META_BARRIER_IMPL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BARRIER_IMPL, MetaBarrierImplClass)) typedef struct _MetaBarrierImpl MetaBarrierImpl; typedef struct _MetaBarrierImplClass MetaBarrierImplClass; struct _MetaBarrierImpl { GObject parent; }; struct _MetaBarrierImplClass { GObjectClass parent_class; gboolean (*is_active) (MetaBarrierImpl *barrier); void (*release) (MetaBarrierImpl *barrier, MetaBarrierEvent *event); void (*destroy) (MetaBarrierImpl *barrier); }; GType meta_barrier_impl_get_type (void) G_GNUC_CONST; 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 */ ukwm/src/backends/meta-logical-monitor.c0000664000175000017500000002007713233511035017215 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "config.h" #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.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; main_output = meta_monitor_get_main_output (monitor); return main_output->crtc->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, 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; if (output->crtc) output->crtc->logical_monitor = logical_monitor; } } logical_monitor->is_presentation = is_presentation; } 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; } static void meta_logical_monitor_init (MetaLogicalMonitor *logical_monitor) { } static void meta_logical_monitor_finalize (GObject *object) { MetaLogicalMonitor *logical_monitor = META_LOGICAL_MONITOR (object); g_list_free (logical_monitor->monitors); G_OBJECT_CLASS (meta_logical_monitor_parent_class)->finalize (object); } static void meta_logical_monitor_class_init (MetaLogicalMonitorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_logical_monitor_finalize; } gboolean meta_logical_monitor_has_neighbor (MetaLogicalMonitor *logical_monitor, MetaLogicalMonitor *neighbor, MetaScreenDirection neighbor_direction) { switch (neighbor_direction) { case META_SCREEN_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_SCREEN_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_SCREEN_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_SCREEN_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; } ukwm/src/backends/meta-cursor-renderer.c0000664000175000017500000002175713233511035017245 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "meta-cursor-renderer.h" #include #include #include #include #include #include "meta-stage.h" struct _MetaCursorRendererPrivate { float current_x; float current_y; MetaCursorSprite *displayed_cursor; MetaOverlay *stage_overlay; gboolean handled_by_backend; guint post_paint_func_id; }; 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); void meta_cursor_renderer_emit_painted (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite) { g_signal_emit (renderer, signals[CURSOR_PAINTED], 0, cursor_sprite); } 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; ClutterRect rect = { 0 }; if (cursor_sprite) rect = meta_cursor_renderer_calculate_rect (renderer, cursor_sprite); /* During early initialization, we can have no stage */ if (!stage) return; 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); } ClutterRect 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 (ClutterRect) { 0 }; 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 (ClutterRect) { .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 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; update_cursor (renderer, cursor_sprite); } void meta_cursor_renderer_force_update (MetaCursorRenderer *renderer) { MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (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; update_cursor (renderer, priv->displayed_cursor); } MetaCursorSprite * meta_cursor_renderer_get_cursor (MetaCursorRenderer *renderer) { MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer); return priv->displayed_cursor; } #ifdef HAVE_WAYLAND void meta_cursor_renderer_realize_cursor_from_wl_buffer (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite, struct wl_resource *buffer) { MetaCursorRendererClass *renderer_class = META_CURSOR_RENDERER_GET_CLASS (renderer); if (renderer_class->realize_cursor_from_wl_buffer) renderer_class->realize_cursor_from_wl_buffer (renderer, cursor_sprite, buffer); } #endif void meta_cursor_renderer_realize_cursor_from_xcursor (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite, XcursorImage *xc_image) { MetaCursorRendererClass *renderer_class = META_CURSOR_RENDERER_GET_CLASS (renderer); if (renderer_class->realize_cursor_from_xcursor) renderer_class->realize_cursor_from_xcursor (renderer, cursor_sprite, xc_image); } ukwm/src/backends/native/0000775000175000017500000000000013254604521014314 5ustar fengfengukwm/src/backends/native/meta-clutter-backend-native.h0000664000175000017500000000275013233511035021742 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #ifndef META_CLUTTER_BACKEND_NATIVE_H #define META_CLUTTER_BACKEND_NATIVE_H #include #include "clutter/clutter.h" #include "clutter/egl/clutter-backend-eglnative.h" #include "backends/native/meta-stage-native.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); #endif /* META_CLUTTER_BACKEND_NATIVE_H */ ukwm/src/backends/native/meta-stage-native.h0000664000175000017500000000300513233511035017770 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #ifndef META_STAGE_NATIVE_H #define META_STAGE_NATIVE_H #include "clutter/clutter-ukwm.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); void meta_stage_native_legacy_set_size (MetaStageNative *stage_native, int width, int height); #endif /* META_STAGE_NATIVE_H */ ukwm/src/backends/native/dbus-utils.c0000664000175000017500000000544013233511035016550 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "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; } ukwm/src/backends/native/meta-launcher.h0000664000175000017500000000271213233511035017206 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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); int meta_launcher_get_kms_fd (MetaLauncher *self); const char * meta_launcher_get_kms_file_path (MetaLauncher *self); #endif /* META_LAUNCHER_H */ ukwm/src/backends/native/meta-backend-native.h0000664000175000017500000000270113233511035020256 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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" #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); #endif /* META_BACKEND_NATIVE_H */ ukwm/src/backends/native/meta-idle-monitor-native.h0000664000175000017500000000404413220600404021266 0ustar fengfeng/* -*- 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_NATIVE_H #define META_IDLE_MONITOR_NATIVE_H #include #include #define META_TYPE_IDLE_MONITOR_NATIVE (meta_idle_monitor_native_get_type ()) #define META_IDLE_MONITOR_NATIVE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_IDLE_MONITOR_NATIVE, MetaIdleMonitorNative)) #define META_IDLE_MONITOR_NATIVE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_IDLE_MONITOR_NATIVE, MetaIdleMonitorNativeClass)) #define META_IS_IDLE_MONITOR_NATIVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_IDLE_MONITOR_NATIVE)) #define META_IS_IDLE_MONITOR_NATIVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_IDLE_MONITOR_NATIVE)) #define META_IDLE_MONITOR_NATIVE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_IDLE_MONITOR_NATIVE, MetaIdleMonitorNativeClass)) typedef struct _MetaIdleMonitorNative MetaIdleMonitorNative; typedef struct _MetaIdleMonitorNativeClass MetaIdleMonitorNativeClass; GType meta_idle_monitor_native_get_type (void); void meta_idle_monitor_native_reset_idletime (MetaIdleMonitor *monitor); #endif /* META_IDLE_MONITOR_NATIVE_H */ ukwm/src/backends/native/meta-launcher.c0000664000175000017500000004023713233511035017205 0ustar fengfeng/* * Copyright (C) 2013 Red Hat, Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "config.h" #include "meta-launcher.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dbus-utils.h" #include "meta-dbus-login1.h" #include "backends/meta-backend-private.h" #include "backends/native/meta-backend-native.h" #include "meta-cursor-renderer-native.h" #include "meta-idle-monitor-native.h" #include "meta-renderer-native.h" #define DRM_CARD_UDEV_DEVICE_TYPE "drm_minor" struct _MetaLauncher { Login1Session *session_proxy; Login1Seat *seat_proxy; GHashTable *sysfs_fds; gboolean session_active; int kms_fd; char *kms_file_path; }; static Login1Session * get_session_proxy (GCancellable *cancellable, GError **error) { g_autofree char *proxy_path = NULL; g_autofree char *session_id = NULL; Login1Session *session_proxy; if (sd_pid_get_session (getpid (), &session_id) < 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Could not get session ID: %m"); 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 (GCancellable *cancellable, GError **error) { Login1Seat *seat = login1_seat_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "org.freedesktop.login1", "/org/freedesktop/login1/seat/self", 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; } static int on_evdev_device_open (const char *path, int flags, gpointer user_data, GError **error) { MetaLauncher *self = user_data; int fd; int major, minor; /* Allow readonly access to sysfs */ if (g_str_has_prefix (path, "/sys/")) { 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; } 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 (self->session_proxy, major, minor, &fd, NULL, error)) return -1; return fd; } static void on_evdev_device_close (int fd, gpointer user_data) { MetaLauncher *self = user_data; int major, minor; GError *error = NULL; 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; } 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 (self->session_proxy, major, minor, NULL, &error)) { g_warning ("Could not release device %d,%d: %s", major, minor, error->message); } out: close (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 guint count_devices_with_connectors (const gchar *seat_name, GList *devices) { g_autoptr (GHashTable) cards = NULL; GList *tmp; cards = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); for (tmp = devices; tmp != NULL; tmp = tmp->next) { GUdevDevice *device = tmp->data; g_autoptr (GUdevDevice) parent_device = NULL; const gchar *parent_device_type = NULL; const gchar *parent_device_name = NULL; const gchar *card_seat; /* filter out the real card devices, we only care about the connectors */ if (g_udev_device_get_device_type (device) != G_UDEV_DEVICE_TYPE_NONE) continue; /* only connectors have a modes attribute */ if (!g_udev_device_has_sysfs_attr (device, "modes")) continue; parent_device = g_udev_device_get_parent (device); if (g_udev_device_get_device_type (parent_device) == G_UDEV_DEVICE_TYPE_CHAR) parent_device_type = g_udev_device_get_property (parent_device, "DEVTYPE"); if (g_strcmp0 (parent_device_type, DRM_CARD_UDEV_DEVICE_TYPE) != 0) continue; card_seat = g_udev_device_get_property (parent_device, "ID_SEAT"); if (!card_seat) card_seat = "seat0"; if (g_strcmp0 (seat_name, card_seat) != 0) continue; parent_device_name = g_udev_device_get_name (parent_device); g_hash_table_insert (cards, (gpointer) parent_device_name , g_steal_pointer (&parent_device)); } return g_hash_table_size (cards); } static gchar * get_primary_gpu_path (const gchar *seat_name) { const gchar *subsystems[] = {"drm", NULL}; gchar *path = NULL; GList *devices, *tmp; g_autoptr (GUdevClient) gudev_client = g_udev_client_new (subsystems); g_autoptr (GUdevEnumerator) enumerator = g_udev_enumerator_new (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) goto out; /* For now, fail on systems where some of the connectors * are connected to secondary gpus. * * https://bugzilla.gnome.org/show_bug.cgi?id=771442 */ if (g_getenv ("UKWM_ALLOW_HYBRID_GPUS") == NULL) { guint num_devices; num_devices = count_devices_with_connectors (seat_name, devices); if (num_devices != 1) goto out; } for (tmp = devices; tmp != NULL; tmp = tmp->next) { g_autoptr (GUdevDevice) platform_device = NULL; g_autoptr (GUdevDevice) pci_device = NULL; GUdevDevice *dev = tmp->data; gint boot_vga; const gchar *device_type; const gchar *device_seat; /* filter out devices that are not character device, like card0-VGA-1 */ if (g_udev_device_get_device_type (dev) != G_UDEV_DEVICE_TYPE_CHAR) continue; device_type = g_udev_device_get_property (dev, "DEVTYPE"); if (g_strcmp0 (device_type, DRM_CARD_UDEV_DEVICE_TYPE) != 0) continue; device_seat = g_udev_device_get_property (dev, "ID_SEAT"); if (!device_seat) { /* when ID_SEAT is not set, it means seat0 */ device_seat = "seat0"; } else if (g_strcmp0 (device_seat, "seat0") != 0) { /* if the device has been explicitly assigned other seat * than seat0, it is probably the right device to use */ path = g_strdup (g_udev_device_get_device_file (dev)); break; } /* skip devices that do not belong to our seat */ if (g_strcmp0 (seat_name, device_seat)) continue; platform_device = g_udev_device_get_parent_with_subsystem (dev, "platform", NULL); if (platform_device != NULL) { path = g_strdup (g_udev_device_get_device_file (dev)); break; } pci_device = g_udev_device_get_parent_with_subsystem (dev, "pci", NULL); if (pci_device != NULL) { /* get value of boot_vga attribute or 0 if the device has no boot_vga */ boot_vga = g_udev_device_get_sysfs_attr_as_int (pci_device, "boot_vga"); if (boot_vga == 1) { /* found the boot_vga device */ path = g_strdup (g_udev_device_get_device_file (dev)); break; } } } out: g_list_free_full (devices, g_object_unref); return path; } static gboolean get_kms_fd (Login1Session *session_proxy, const gchar *seat_id, int *fd_out, char **kms_file_path_out, GError **error) { int major, minor; int fd; g_autofree gchar *path = get_primary_gpu_path (seat_id); if (!path) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "could not find drm kms device"); return FALSE; } 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 FALSE; } if (!take_device (session_proxy, major, minor, &fd, NULL, error)) { g_prefix_error (error, "Could not open DRM device: "); return FALSE; } *fd_out = fd; *kms_file_path_out = g_steal_pointer (&path); return TRUE; } static gchar * get_seat_id (GError **error) { g_autofree char *session_id = NULL; char *seat_id = NULL; int r; r = sd_pid_get_session (0, &session_id); if (r < 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Could not get session for PID: %s", g_strerror (-r)); 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; int kms_fd; char *kms_file_path; 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 (NULL, error); if (!seat_proxy) goto fail; if (!get_kms_fd (session_proxy, seat_id, &kms_fd, &kms_file_path, error)) goto fail; self = g_slice_new0 (MetaLauncher); self->session_proxy = g_object_ref (session_proxy); self->seat_proxy = g_object_ref (seat_proxy); self->sysfs_fds = g_hash_table_new (NULL, NULL); self->session_active = TRUE; self->kms_fd = kms_fd; self->kms_file_path = kms_file_path; clutter_evdev_set_seat_id (seat_id); clutter_evdev_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_object_unref (self->seat_proxy); g_object_unref (self->session_proxy); g_hash_table_destroy (self->sysfs_fds); g_free (self->kms_file_path); 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); } int meta_launcher_get_kms_fd (MetaLauncher *self) { return self->kms_fd; } const char * meta_launcher_get_kms_file_path (MetaLauncher *self) { return self->kms_file_path; } ukwm/src/backends/native/meta-stage-native.c0000664000175000017500000002347613233511035020001 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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; CoglOnscreen *pending_onscreen; CoglClosure *frame_closure; int64_t presented_frame_counter_sync; int64_t presented_frame_counter_complete; gboolean pending_resize; int pending_width; int pending_height; }; static ClutterStageWindowIface *clutter_stage_window_parent_iface = NULL; static void clutter_stage_window_iface_init (ClutterStageWindowIface *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 MetaRendererView * get_legacy_view (MetaRenderer *renderer) { GList *views; views = meta_renderer_get_views (renderer); g_assert (g_list_length (views) <= 1); if (views) return views->data; else return NULL; } 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); meta_renderer_rebuild_views (renderer); meta_renderer_native_queue_modes_reset (META_RENDERER_NATIVE (renderer)); ensure_frame_callbacks (stage_native); } void meta_stage_native_legacy_set_size (MetaStageNative *stage_native, int width, int height) { stage_native->pending_resize = TRUE; stage_native->pending_width = width; stage_native->pending_height = height; } static void meta_stage_native_unrealize (ClutterStageWindow *stage_window) { MetaStageNative *stage_native = META_STAGE_NATIVE (stage_window); clutter_stage_window_parent_iface->unrealize (stage_window); g_clear_pointer (&stage_native->pending_onscreen, cogl_object_unref); } 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 void ensure_legacy_view (ClutterStageWindow *stage_window) { MetaStageNative *stage_native = META_STAGE_NATIVE (stage_window); MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer); MetaRendererView *legacy_view; legacy_view = get_legacy_view (renderer); if (legacy_view) return; legacy_view = meta_renderer_native_create_legacy_view (renderer_native); if (!legacy_view) return; meta_renderer_set_legacy_view (renderer, legacy_view); ensure_frame_callback (stage_native, CLUTTER_STAGE_VIEW (legacy_view)); } static GList * meta_stage_native_get_views (ClutterStageWindow *stage_window) { MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); if (!meta_is_stage_views_enabled ()) ensure_legacy_view (stage_window); 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 maybe_resize_legacy_view (MetaStageNative *stage_native) { MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer); MetaRendererView *legacy_view; int width = stage_native->pending_width; int height = stage_native->pending_height; GError *error = NULL; if (!stage_native->pending_resize) return; stage_native->pending_resize = FALSE; legacy_view = get_legacy_view (renderer); if (!legacy_view) return; if (!meta_renderer_native_set_legacy_view_size (renderer_native, legacy_view, width, height, &error)) { meta_warning ("Applying display configuration failed: %s\n", error->message); g_error_free (error); } } static void meta_stage_native_redraw (ClutterStageWindow *stage_window) { MetaStageNative *stage_native = META_STAGE_NATIVE (stage_window); maybe_resize_legacy_view (stage_native); clutter_stage_window_parent_iface->redraw (stage_window); } 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 (ClutterStageWindowIface *iface) { clutter_stage_window_parent_iface = g_type_interface_peek_parent (iface); iface->unrealize = meta_stage_native_unrealize; 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->redraw = meta_stage_native_redraw; iface->finish_frame = meta_stage_native_finish_frame; } ukwm/src/backends/native/meta-input-settings-native.c0000664000175000017500000005224713233511035021671 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author: Carlos Garnacho */ #include "config.h" #include #include #include #include "meta-backend-native.h" #include "meta-input-settings-native.h" #include "backends/meta-logical-monitor.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, GDesktopDeviceSendEvents mode) { enum libinput_config_send_events_mode libinput_mode; struct libinput_device *libinput_device; switch (mode) { case G_DESKTOP_DEVICE_SEND_EVENTS_DISABLED: libinput_mode = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; break; case G_DESKTOP_DEVICE_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE: libinput_mode = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; break; case G_DESKTOP_DEVICE_SEND_EVENTS_ENABLED: libinput_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; break; default: g_assert_not_reached (); } libinput_device = clutter_evdev_input_device_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 = clutter_evdev_input_device_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 = clutter_evdev_input_device_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 = clutter_evdev_input_device_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 = clutter_evdev_input_device_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 = clutter_evdev_input_device_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 = clutter_evdev_input_device_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 = clutter_evdev_input_device_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 = clutter_evdev_input_device_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 = clutter_evdev_input_device_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 = clutter_evdev_input_device_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, GDesktopTouchpadClickMethod mode) { enum libinput_config_click_method click_method = 0; struct libinput_device *libinput_device; libinput_device = clutter_evdev_input_device_get_libinput_device (device); if (!libinput_device) return; switch (mode) { case G_DESKTOP_TOUCHPAD_CLICK_METHOD_DEFAULT: click_method = libinput_device_config_click_get_default_method (libinput_device); break; case G_DESKTOP_TOUCHPAD_CLICK_METHOD_NONE: click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE; break; case G_DESKTOP_TOUCHPAD_CLICK_METHOD_AREAS: click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; break; case G_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) { ClutterDeviceManager *manager = clutter_device_manager_get_default (); clutter_evdev_set_keyboard_repeat (manager, enabled, delay, interval); } static void set_device_accel_profile (ClutterInputDevice *device, GDesktopPointerAccelProfile profile) { struct libinput_device *libinput_device; enum libinput_config_accel_profile libinput_profile; uint32_t profiles; libinput_device = clutter_evdev_input_device_get_libinput_device (device); switch (profile) { case G_DESKTOP_POINTER_ACCEL_PROFILE_FLAT: libinput_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT; break; case G_DESKTOP_POINTER_ACCEL_PROFILE_ADAPTIVE: libinput_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; break; default: g_warn_if_reached (); case G_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 = clutter_evdev_input_device_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 is_trackball_device (ClutterInputDevice *device) { return meta_input_device_is_trackball (device); } static void meta_input_settings_native_set_mouse_accel_profile (MetaInputSettings *settings, ClutterInputDevice *device, GDesktopPointerAccelProfile 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, GDesktopPointerAccelProfile profile) { if (!is_trackball_device (device)) return; set_device_accel_profile (device, profile); } static void meta_input_settings_native_set_tablet_mapping (MetaInputSettings *settings, ClutterInputDevice *device, GDesktopTabletMapping mapping) { ClutterInputDeviceMapping dev_mapping; if (mapping == G_DESKTOP_TABLET_MAPPING_ABSOLUTE) dev_mapping = CLUTTER_INPUT_DEVICE_MAPPING_ABSOLUTE; else if (mapping == G_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 = clutter_evdev_input_device_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; clutter_evdev_input_device_tool_set_pressure_curve (tool, pressure_curve); } static guint action_to_evcode (GDesktopStylusButtonAction action) { switch (action) { case G_DESKTOP_STYLUS_BUTTON_ACTION_MIDDLE: return BTN_STYLUS; case G_DESKTOP_STYLUS_BUTTON_ACTION_RIGHT: return BTN_STYLUS2; case G_DESKTOP_STYLUS_BUTTON_ACTION_BACK: return BTN_BACK; case G_DESKTOP_STYLUS_BUTTON_ACTION_FORWARD: return BTN_FORWARD; case G_DESKTOP_STYLUS_BUTTON_ACTION_DEFAULT: default: return 0; } } static void meta_input_settings_native_set_stylus_button_map (MetaInputSettings *settings, ClutterInputDevice *device, ClutterInputDeviceTool *tool, GDesktopStylusButtonAction primary, GDesktopStylusButtonAction secondary) { clutter_evdev_input_device_tool_set_button_code (tool, CLUTTER_BUTTON_MIDDLE, action_to_evcode (primary)); clutter_evdev_input_device_tool_set_button_code (tool, CLUTTER_BUTTON_SECONDARY, action_to_evcode (secondary)); } 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->has_two_finger_scroll = meta_input_settings_native_has_two_finger_scroll; } static void meta_input_settings_native_init (MetaInputSettingsNative *settings) { } ukwm/src/backends/native/meta-cursor-renderer-native.c0000664000175000017500000007051213233511035022010 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "meta-cursor-renderer-native.h" #include #include #include #include #include #include #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor.h" #include "backends/meta-monitor-manager-private.h" #include "backends/native/meta-renderer-native.h" #include "core/boxes-private.h" #include "meta/boxes.h" #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 _MetaCursorRendererNativePrivate { gboolean hw_state_invalidated; gboolean has_hw_cursor; gboolean hw_cursor_broken; MetaCursorSprite *last_cursor; guint animation_timeout_id; int drm_fd; struct gbm_device *gbm; uint64_t cursor_width; uint64_t cursor_height; }; typedef struct _MetaCursorRendererNativePrivate MetaCursorRendererNativePrivate; typedef enum _MetaCursorGbmBoState { META_CURSOR_GBM_BO_STATE_NONE, META_CURSOR_GBM_BO_STATE_SET, META_CURSOR_GBM_BO_STATE_INVALIDATED, } MetaCursorGbmBoState; typedef struct _MetaCursorNativePrivate { guint active_bo; MetaCursorGbmBoState pending_bo_state; struct gbm_bo *bos[HW_CURSOR_BUFFER_COUNT]; } MetaCursorNativePrivate; G_DEFINE_TYPE_WITH_PRIVATE (MetaCursorRendererNative, meta_cursor_renderer_native, META_TYPE_CURSOR_RENDERER); static MetaCursorNativePrivate * ensure_cursor_priv (MetaCursorSprite *cursor_sprite); 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); if (priv->animation_timeout_id) g_source_remove (priv->animation_timeout_id); G_OBJECT_CLASS (meta_cursor_renderer_native_parent_class)->finalize (object); } static guint get_pending_cursor_sprite_gbm_bo_index (MetaCursorSprite *cursor_sprite) { MetaCursorNativePrivate *cursor_priv = g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite); return (cursor_priv->active_bo + 1) % HW_CURSOR_BUFFER_COUNT; } static struct gbm_bo * get_pending_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite) { MetaCursorNativePrivate *cursor_priv = g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite); guint pending_bo; if (!cursor_priv) return NULL; pending_bo = get_pending_cursor_sprite_gbm_bo_index (cursor_sprite); return cursor_priv->bos[pending_bo]; } static struct gbm_bo * get_active_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite) { MetaCursorNativePrivate *cursor_priv = g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite); if (!cursor_priv) return NULL; return cursor_priv->bos[cursor_priv->active_bo]; } static void set_pending_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite, struct gbm_bo *bo) { MetaCursorNativePrivate *cursor_priv; guint pending_bo; cursor_priv = ensure_cursor_priv (cursor_sprite); pending_bo = get_pending_cursor_sprite_gbm_bo_index (cursor_sprite); cursor_priv->bos[pending_bo] = bo; cursor_priv->pending_bo_state = META_CURSOR_GBM_BO_STATE_SET; } static void set_crtc_cursor (MetaCursorRendererNative *native, MetaCrtc *crtc, MetaCursorSprite *cursor_sprite) { MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); if (cursor_sprite) { MetaCursorNativePrivate *cursor_priv = g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite); struct gbm_bo *bo; union gbm_bo_handle handle; int hot_x, hot_y; if (cursor_priv->pending_bo_state == META_CURSOR_GBM_BO_STATE_SET) bo = get_pending_cursor_sprite_gbm_bo (cursor_sprite); else bo = get_active_cursor_sprite_gbm_bo (cursor_sprite); if (!priv->hw_state_invalidated && bo == crtc->cursor_renderer_private) return; crtc->cursor_renderer_private = bo; handle = gbm_bo_get_handle (bo); meta_cursor_sprite_get_hotspot (cursor_sprite, &hot_x, &hot_y); if (drmModeSetCursor2 (priv->drm_fd, crtc->crtc_id, handle.u32, priv->cursor_width, priv->cursor_height, hot_x, hot_y) < 0) { g_warning ("drmModeSetCursor2 failed with (%s), " "drawing cursor with OpenGL from now on", strerror (errno)); priv->has_hw_cursor = FALSE; priv->hw_cursor_broken = TRUE; } if (cursor_priv->pending_bo_state == META_CURSOR_GBM_BO_STATE_SET) { cursor_priv->active_bo = (cursor_priv->active_bo + 1) % HW_CURSOR_BUFFER_COUNT; cursor_priv->pending_bo_state = META_CURSOR_GBM_BO_STATE_NONE; } } else { if (priv->hw_state_invalidated || crtc->cursor_renderer_private != NULL) { drmModeSetCursor2 (priv->drm_fd, crtc->crtc_id, 0, 0, 0, 0, 0); crtc->cursor_renderer_private = NULL; } } } typedef struct { MetaCursorRendererNative *in_cursor_renderer_native; MetaLogicalMonitor *in_logical_monitor; ClutterRect in_local_cursor_rect; MetaCursorSprite *in_cursor_sprite; 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); ClutterRect scaled_crtc_rect; float scale; int crtc_x, crtc_y; if (meta_is_stage_views_scaled ()) scale = meta_logical_monitor_get_scale (data->in_logical_monitor); else scale = 1.0; meta_monitor_calculate_crtc_pos (monitor, monitor_mode, monitor_crtc_mode->output, META_MONITOR_TRANSFORM_NORMAL, &crtc_x, &crtc_y); scaled_crtc_rect = (ClutterRect) { .origin = { .x = crtc_x / scale, .y = crtc_y / scale }, .size = { .width = monitor_crtc_mode->crtc_mode->width / scale, .height = monitor_crtc_mode->crtc_mode->height / scale }, }; if (priv->has_hw_cursor && clutter_rect_intersection (&scaled_crtc_rect, &data->in_local_cursor_rect, NULL)) { float crtc_cursor_x, crtc_cursor_y; set_crtc_cursor (data->in_cursor_renderer_native, monitor_crtc_mode->output->crtc, data->in_cursor_sprite); 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; drmModeMoveCursor (priv->drm_fd, monitor_crtc_mode->output->crtc->crtc_id, roundf (crtc_cursor_x), roundf (crtc_cursor_y)); data->out_painted = data->out_painted || TRUE; } else { set_crtc_cursor (data->in_cursor_renderer_native, monitor_crtc_mode->output->crtc, NULL); } return 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 = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); GList *logical_monitors; GList *l; ClutterRect rect; gboolean painted = FALSE; if (cursor_sprite) rect = meta_cursor_renderer_calculate_rect (renderer, cursor_sprite); else rect = (ClutterRect) { 0 }; 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 = (ClutterRect) { .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 }; 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; } 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) { MetaCursorNativePrivate *cursor_priv = g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite); if (!cursor_priv) return FALSE; switch (cursor_priv->pending_bo_state) { case META_CURSOR_GBM_BO_STATE_NONE: return get_active_cursor_sprite_gbm_bo (cursor_sprite) != 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 gboolean cursor_over_transformed_logical_monitor (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); GList *logical_monitors; GList *l; ClutterRect 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; ClutterRect logical_monitor_rect; MetaMonitorTransform transform; logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor); logical_monitor_rect = meta_rectangle_to_clutter_rect (&logical_monitor_layout); if (!clutter_rect_intersection (&cursor_rect, &logical_monitor_rect, NULL)) continue; transform = meta_logical_monitor_get_transform (logical_monitor); if (transform != META_MONITOR_TRANSFORM_NORMAL) return TRUE; } return FALSE; } static float calculate_cursor_crtc_sprite_scale (MetaCursorSprite *cursor_sprite, MetaLogicalMonitor *logical_monitor) { return (meta_logical_monitor_get_scale (logical_monitor) * meta_cursor_sprite_get_texture_scale (cursor_sprite)); } static gboolean can_draw_cursor_unscaled (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite) { MetaBackend *backend; MetaMonitorManager *monitor_manager; ClutterRect cursor_rect; GList *logical_monitors; GList *l; gboolean has_visible_crtc_sprite = FALSE; if (!meta_is_stage_views_scaled ()) return meta_cursor_sprite_get_texture_scale (cursor_sprite) == 1.0; backend = meta_get_backend (); monitor_manager = meta_backend_get_monitor_manager (backend); logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); if (!logical_monitors) return FALSE; cursor_rect = meta_cursor_renderer_calculate_rect (renderer, cursor_sprite); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; ClutterRect logical_monitor_rect = meta_rectangle_to_clutter_rect (&logical_monitor->rect); if (!clutter_rect_intersection (&cursor_rect, &logical_monitor_rect, NULL)) continue; if (calculate_cursor_crtc_sprite_scale (cursor_sprite, logical_monitor) != 1.0) return FALSE; has_visible_crtc_sprite = TRUE; } return has_visible_crtc_sprite; } static gboolean should_have_hw_cursor (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite) { MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer); MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); CoglTexture *texture; if (priv->hw_cursor_broken) return FALSE; if (!cursor_sprite) return FALSE; if (cursor_over_transformed_logical_monitor (renderer, cursor_sprite)) return FALSE; texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite); if (!texture) return FALSE; if (!can_draw_cursor_unscaled (renderer, cursor_sprite)) return FALSE; if (!has_valid_cursor_sprite_gbm_bo (cursor_sprite)) return FALSE; 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 meta_cursor_renderer_native_trigger_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; if (priv->animation_timeout_id) { g_source_remove (priv->animation_timeout_id); priv->animation_timeout_id = 0; } 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, "[ukwm] meta_cursor_renderer_native_update_animation"); } } 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); if (cursor_sprite) meta_cursor_sprite_realize_texture (cursor_sprite); meta_cursor_renderer_native_trigger_frame (native, cursor_sprite); priv->has_hw_cursor = should_have_hw_cursor (renderer, cursor_sprite); update_hw_cursor (native, cursor_sprite); return priv->has_hw_cursor; } static void get_hardware_cursor_size (MetaCursorRendererNative *native, uint64_t *width, uint64_t *height) { MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); *width = priv->cursor_width; *height = priv->cursor_height; } static void cursor_priv_free (gpointer data) { MetaCursorNativePrivate *cursor_priv = data; guint i; if (!data) return; for (i = 0; i < HW_CURSOR_BUFFER_COUNT; i++) g_clear_pointer (&cursor_priv->bos[0], (GDestroyNotify) gbm_bo_destroy); g_slice_free (MetaCursorNativePrivate, cursor_priv); } static MetaCursorNativePrivate * ensure_cursor_priv (MetaCursorSprite *cursor_sprite) { MetaCursorNativePrivate *cursor_priv = g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite); if (!cursor_priv) { cursor_priv = g_slice_new0 (MetaCursorNativePrivate); g_object_set_qdata_full (G_OBJECT (cursor_sprite), quark_cursor_sprite, cursor_priv, cursor_priv_free); } return cursor_priv; } static void load_cursor_sprite_gbm_buffer (MetaCursorRendererNative *native, MetaCursorSprite *cursor_sprite, uint8_t *pixels, uint width, uint height, int rowstride, uint32_t gbm_format) { MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); uint64_t cursor_width, cursor_height; get_hardware_cursor_size (native, &cursor_width, &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; } if (gbm_device_is_format_supported (priv->gbm, 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 (priv->gbm, 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, bo); } else { meta_warning ("HW cursor for format %d not supported\n", gbm_format); } } static void invalidate_pending_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite) { MetaCursorNativePrivate *cursor_priv = g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite); guint pending_bo; if (!cursor_priv) return; pending_bo = get_pending_cursor_sprite_gbm_bo_index (cursor_sprite); g_clear_pointer (&cursor_priv->bos[pending_bo], (GDestroyNotify) gbm_bo_destroy); cursor_priv->pending_bo_state = META_CURSOR_GBM_BO_STATE_INVALIDATED; } #ifdef HAVE_WAYLAND static void meta_cursor_renderer_native_realize_cursor_from_wl_buffer (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite, struct wl_resource *buffer) { MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer); MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); uint32_t gbm_format; uint64_t cursor_width, cursor_height; CoglTexture *texture; uint width, height; if (!priv->gbm || priv->hw_cursor_broken) return; /* Destroy any previous pending cursor buffer; we'll always either fail (which * should unset, or succeed, which will set new buffer. */ invalidate_pending_cursor_sprite_gbm_bo (cursor_sprite); texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite); width = cogl_texture_get_width (texture); height = cogl_texture_get_height (texture); struct wl_shm_buffer *shm_buffer = wl_shm_buffer_get (buffer); if (shm_buffer) { int rowstride = wl_shm_buffer_get_stride (shm_buffer); uint8_t *buffer_data; wl_shm_buffer_begin_access (shm_buffer); switch (wl_shm_buffer_get_format (shm_buffer)) { #if G_BYTE_ORDER == G_BIG_ENDIAN case WL_SHM_FORMAT_ARGB8888: gbm_format = GBM_FORMAT_ARGB8888; break; case WL_SHM_FORMAT_XRGB8888: gbm_format = GBM_FORMAT_XRGB8888; break; #else case WL_SHM_FORMAT_ARGB8888: gbm_format = GBM_FORMAT_ARGB8888; break; case WL_SHM_FORMAT_XRGB8888: gbm_format = GBM_FORMAT_XRGB8888; break; #endif default: g_warn_if_reached (); gbm_format = GBM_FORMAT_ARGB8888; } buffer_data = wl_shm_buffer_get_data (shm_buffer); load_cursor_sprite_gbm_buffer (native, cursor_sprite, buffer_data, width, height, rowstride, gbm_format); wl_shm_buffer_end_access (shm_buffer); } else { 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. */ get_hardware_cursor_size (native, &cursor_width, &cursor_height); if (width != cursor_width || height != cursor_height) { meta_warning ("Invalid cursor size (must be 64x64), falling back to software (GL) cursors\n"); return; } bo = gbm_bo_import (priv->gbm, GBM_BO_IMPORT_WL_BUFFER, buffer, GBM_BO_USE_CURSOR); if (!bo) { meta_warning ("Importing HW cursor from wl_buffer failed\n"); return; } set_pending_cursor_sprite_gbm_bo (cursor_sprite, bo); } } #endif static void meta_cursor_renderer_native_realize_cursor_from_xcursor (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite, XcursorImage *xc_image) { MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer); MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); if (!priv->gbm || priv->hw_cursor_broken) return; invalidate_pending_cursor_sprite_gbm_bo (cursor_sprite); load_cursor_sprite_gbm_buffer (native, cursor_sprite, (uint8_t *) xc_image->pixels, xc_image->width, xc_image->height, xc_image->width * 4, GBM_FORMAT_ARGB8888); } 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; #ifdef HAVE_WAYLAND renderer_class->realize_cursor_from_wl_buffer = meta_cursor_renderer_native_realize_cursor_from_wl_buffer; #endif renderer_class->realize_cursor_from_xcursor = meta_cursor_renderer_native_realize_cursor_from_xcursor; quark_cursor_sprite = g_quark_from_static_string ("-meta-cursor-native"); } 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; update_hw_cursor (native, meta_cursor_renderer_get_cursor (renderer)); } static void on_monitors_changed (MetaMonitorManager *monitors, MetaCursorRendererNative *native) { /* Our tracking is all messed up, so force an update. */ force_update_hw_cursor (native); } static void meta_cursor_renderer_native_init (MetaCursorRendererNative *native) { MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); MetaMonitorManager *monitors; monitors = meta_monitor_manager_get (); g_signal_connect_object (monitors, "monitors-changed-internal", G_CALLBACK (on_monitors_changed), native, 0); priv->hw_state_invalidated = TRUE; #if defined(CLUTTER_WINDOWING_EGL) if (clutter_check_windowing_backend (CLUTTER_WINDOWING_EGL)) { MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer); priv->drm_fd = meta_renderer_native_get_kms_fd (renderer_native); priv->gbm = meta_renderer_native_get_gbm (renderer_native); uint64_t width, height; if (drmGetCap (priv->drm_fd, DRM_CAP_CURSOR_WIDTH, &width) == 0 && drmGetCap (priv->drm_fd, DRM_CAP_CURSOR_HEIGHT, &height) == 0) { priv->cursor_width = width; priv->cursor_height = height; } else { priv->cursor_width = 64; priv->cursor_height = 64; } } #endif } struct gbm_device * meta_cursor_renderer_native_get_gbm_device (MetaCursorRendererNative *native) { MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); return priv->gbm; } void meta_cursor_renderer_native_force_update (MetaCursorRendererNative *native) { force_update_hw_cursor (native); } ukwm/src/backends/native/meta-renderer-native.c0000664000175000017500000021572113220600404020473 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2011 Intel Corporation. * Copyright (C) 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 shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION 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 "backends/meta-backend-private.h" #include "backends/meta-egl.h" #include "backends/meta-egl-ext.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-renderer-view.h" #include "backends/native/meta-monitor-manager-kms.h" #include "backends/native/meta-renderer-native.h" #include "cogl/cogl.h" #include "core/boxes-private.h" #ifndef EGL_DRM_MASTER_FD_EXT #define EGL_DRM_MASTER_FD_EXT 0x333C #endif enum { PROP_0, PROP_KMS_FD, PROP_KMS_FILE_PATH, PROP_LAST }; typedef struct _MetaOnscreenNative { struct { struct gbm_surface *surface; uint32_t current_fb_id; uint32_t next_fb_id; struct gbm_bo *current_bo; struct gbm_bo *next_bo; } gbm; #ifdef HAVE_EGL_DEVICE struct { EGLStreamKHR stream; struct { uint32_t fb_id; uint32_t handle; void *map; uint64_t map_size; } dumb_fb; } egl; #endif gboolean pending_queue_swap_notify; 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 pending_flips; } MetaOnscreenNative; struct _MetaRendererNative { MetaRenderer parent; int kms_fd; char *kms_file_path; MetaRendererNativeMode mode; EGLDisplay egl_display; struct { struct gbm_device *device; } gbm; #ifdef HAVE_EGL_DEVICE struct { EGLDeviceEXT device; gboolean no_egl_output_drm_flip_event; } egl; #endif CoglClosure *swap_notify_idle; int64_t frame_counter; gboolean pending_unset_disabled_crtcs; gboolean no_add_fb2; }; 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 meta_renderer_native_disconnect (CoglRenderer *cogl_renderer) { CoglRendererEGL *egl_renderer = cogl_renderer->winsys; if (egl_renderer->edpy != EGL_NO_DISPLAY) eglTerminate (egl_renderer->edpy); g_slice_free (CoglRendererEGL, egl_renderer); } static void flush_pending_swap_notify (CoglFramebuffer *framebuffer) { if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN) { CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreenEGL *egl_onscreen = onscreen->winsys; MetaOnscreenNative *onscreen_native = egl_onscreen->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 *egl_renderer = cogl_context->display->renderer->winsys; MetaRendererNative *renderer_native = egl_renderer->platform; 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_bo (CoglOnscreen *onscreen) { CoglOnscreenEGL *egl_onscreen = onscreen->winsys; MetaOnscreenNative *onscreen_native = egl_onscreen->platform; CoglContext *cogl_context = COGL_FRAMEBUFFER (onscreen)->context; CoglRenderer *cogl_renderer = cogl_context->display->renderer; CoglRendererEGL *egl_renderer = cogl_renderer->winsys; MetaRendererNative *renderer_native = egl_renderer->platform; if (onscreen_native->gbm.current_fb_id) { drmModeRmFB (renderer_native->kms_fd, onscreen_native->gbm.current_fb_id); onscreen_native->gbm.current_fb_id = 0; } if (onscreen_native->gbm.current_bo) { gbm_surface_release_buffer (onscreen_native->gbm.surface, onscreen_native->gbm.current_bo); onscreen_native->gbm.current_bo = NULL; } } static void meta_onscreen_native_queue_swap_notify (CoglOnscreen *onscreen) { CoglOnscreenEGL *egl_onscreen = onscreen->winsys; MetaOnscreenNative *onscreen_native = egl_onscreen->platform; MetaBackend *backend = meta_get_backend (); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); CoglRenderer *cogl_renderer = cogl_context->display->renderer; CoglRendererEGL *egl_renderer = cogl_renderer->winsys; MetaRendererNative *renderer_native = egl_renderer->platform; 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) { 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) { MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer); CoglRendererEGL *egl_renderer; cogl_renderer->winsys = g_slice_new0 (CoglRendererEGL); egl_renderer = cogl_renderer->winsys; egl_renderer->platform_vtable = &_cogl_winsys_egl_vtable; egl_renderer->platform = renderer_native; egl_renderer->edpy = renderer_native->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 *egl_renderer = cogl_display->renderer->winsys; MetaRendererNative *renderer_native = egl_renderer->platform; int i = 0; switch (renderer_native->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 meta_renderer_native_setup_egl_display (CoglDisplay *cogl_display, GError **error) { CoglDisplayEGL *egl_display = cogl_display->winsys; CoglRendererEGL *egl_renderer = cogl_display->renderer->winsys; MetaRendererNative *renderer_native = egl_renderer->platform; egl_display->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_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 *egl_display = cogl_display->winsys; CoglRenderer *cogl_renderer = cogl_display->renderer; CoglRendererEGL *egl_renderer = cogl_renderer->winsys; if ((egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_SURFACELESS_CONTEXT) == 0) { egl_display->dummy_surface = create_dummy_pbuffer_surface (egl_renderer->edpy, error); if (egl_display->dummy_surface == EGL_NO_SURFACE) return FALSE; } if (!_cogl_winsys_egl_make_current (cogl_display, egl_display->dummy_surface, egl_display->dummy_surface, egl_display->egl_context)) { _cogl_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 *egl_display = cogl_display->winsys; CoglRenderer *cogl_renderer = cogl_display->renderer; CoglRendererEGL *egl_renderer = cogl_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; } } static void meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen) { CoglOnscreenEGL *egl_onscreen = onscreen->winsys; MetaOnscreenNative *onscreen_native = egl_onscreen->platform; free_current_bo (onscreen); onscreen_native->gbm.current_fb_id = onscreen_native->gbm.next_fb_id; onscreen_native->gbm.next_fb_id = 0; onscreen_native->gbm.current_bo = onscreen_native->gbm.next_bo; onscreen_native->gbm.next_bo = NULL; } static void on_crtc_flipped (GClosure *closure, MetaRendererView *view) { ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view); CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (stage_view); CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreenEGL *egl_onscreen = onscreen->winsys; MetaOnscreenNative *onscreen_native = egl_onscreen->platform; MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer); onscreen_native->pending_flips--; if (onscreen_native->pending_flips == 0) { onscreen_native->pending_queue_swap_notify = FALSE; meta_onscreen_native_queue_swap_notify (onscreen); switch (renderer_native->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 void flip_closure_destroyed (MetaRendererView *view) { ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view); CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (stage_view); CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreenEGL *egl_onscreen = onscreen->winsys; MetaOnscreenNative *onscreen_native = egl_onscreen->platform; MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer); switch (renderer_native->mode) { case META_RENDERER_NATIVE_MODE_GBM: if (onscreen_native->gbm.next_fb_id) { drmModeRmFB (renderer_native->kms_fd, onscreen_native->gbm.next_fb_id); gbm_surface_release_buffer (onscreen_native->gbm.surface, onscreen_native->gbm.next_bo); onscreen_native->gbm.next_bo = NULL; onscreen_native->gbm.next_fb_id = 0; } break; #ifdef HAVE_EGL_DEVICE case META_RENDERER_NATIVE_MODE_EGL_DEVICE: break; #endif } if (onscreen_native->pending_queue_swap_notify) { meta_onscreen_native_queue_swap_notify (onscreen); onscreen_native->pending_queue_swap_notify = FALSE; } g_object_unref (view); } #ifdef HAVE_EGL_DEVICE static gboolean flip_egl_stream (MetaRendererNative *renderer_native, MetaOnscreenNative *onscreen_native, GClosure *flip_closure) { 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); CoglDisplay *cogl_display = cogl_context->display; CoglRendererEGL *egl_renderer = cogl_display->renderer->winsys; EGLAttrib *acquire_attribs; GError *error = NULL; if (renderer_native->egl.no_egl_output_drm_flip_event) return FALSE; acquire_attribs = (EGLAttrib[]) { EGL_DRM_FLIP_EVENT_DATA_NV, (EGLAttrib) flip_closure, EGL_NONE }; if (!meta_egl_stream_consumer_acquire_attrib (egl, egl_renderer->edpy, onscreen_native->egl.stream, acquire_attribs, &error)) { if (error->domain != META_EGL_ERROR || error->code != EGL_RESOURCE_BUSY_EXT) { g_warning ("Failed to flip EGL stream (%s), relying on clock from " "now on", error->message); renderer_native->egl.no_egl_output_drm_flip_event = TRUE; } g_error_free (error); return FALSE; } g_closure_ref (flip_closure); return TRUE; } #endif /* HAVE_EGL_DEVICE */ static void meta_onscreen_native_flip_crtc (MetaOnscreenNative *onscreen_native, GClosure *flip_closure, MetaCrtc *crtc, int x, int y, gboolean *fb_in_use) { MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerKms *monitor_manager_kms = META_MONITOR_MANAGER_KMS (monitor_manager); if (!meta_monitor_manager_kms_is_crtc_active (monitor_manager_kms, crtc)) { *fb_in_use = FALSE; return; } switch (renderer_native->mode) { case META_RENDERER_NATIVE_MODE_GBM: if (meta_monitor_manager_kms_flip_crtc (monitor_manager_kms, crtc, x, y, onscreen_native->gbm.next_fb_id, flip_closure, fb_in_use)) onscreen_native->pending_flips++; break; #ifdef HAVE_EGL_DEVICE case META_RENDERER_NATIVE_MODE_EGL_DEVICE: if (flip_egl_stream (renderer_native, onscreen_native, flip_closure)) onscreen_native->pending_flips++; *fb_in_use = TRUE; break; #endif } } static void meta_onscreen_native_set_crtc_modes (MetaOnscreenNative *onscreen_native) { MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerKms *monitor_manager_kms = META_MONITOR_MANAGER_KMS (monitor_manager); MetaRendererView *view = onscreen_native->view; uint32_t fb_id = 0; MetaLogicalMonitor *logical_monitor; switch (renderer_native->mode) { case META_RENDERER_NATIVE_MODE_GBM: fb_id = onscreen_native->gbm.next_fb_id; break; #ifdef HAVE_EGL_DEVICE case META_RENDERER_NATIVE_MODE_EGL_DEVICE: fb_id = onscreen_native->egl.dumb_fb.fb_id; break; #endif } g_assert (fb_id != 0); logical_monitor = meta_renderer_view_get_logical_monitor (view); if (logical_monitor) { unsigned int i; for (i = 0; i < monitor_manager->n_crtcs; i++) { MetaCrtc *crtc = &monitor_manager->crtcs[i]; int x = crtc->rect.x - logical_monitor->rect.x; int y = crtc->rect.y - logical_monitor->rect.y; if (crtc->logical_monitor != logical_monitor) continue; meta_monitor_manager_kms_apply_crtc_mode (monitor_manager_kms, crtc, x, y, fb_id); } } else { unsigned int i; for (i = 0; i < monitor_manager->n_crtcs; i++) { MetaCrtc *crtc = &monitor_manager->crtcs[i]; meta_monitor_manager_kms_apply_crtc_mode (monitor_manager_kms, crtc, crtc->rect.x, crtc->rect.y, fb_id); } } } static void meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen) { CoglOnscreenEGL *egl_onscreen = onscreen->winsys; MetaOnscreenNative *onscreen_native = egl_onscreen->platform; MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaRendererView *view = onscreen_native->view; GClosure *flip_closure; MetaLogicalMonitor *logical_monitor; gboolean fb_in_use = FALSE; /* * Create a closure that either will be invoked or destructed. * Invoking the closure represents a completed flip. If the closure * is destructed before being invoked, the framebuffer references will be * cleaned up accordingly. * * Each successful flip will each own one reference to the closure, thus keep * it alive until either invoked or destructed. If flipping failed, the * closure will be destructed before this function goes out of scope. */ flip_closure = g_cclosure_new (G_CALLBACK (on_crtc_flipped), g_object_ref (view), (GClosureNotify) flip_closure_destroyed); g_closure_set_marshal (flip_closure, g_cclosure_marshal_VOID__VOID); /* Either flip the CRTC's of the monitor info, if we are drawing just part * of the stage, or all of the CRTC's if we are drawing the whole stage. */ logical_monitor = meta_renderer_view_get_logical_monitor (view); if (logical_monitor) { unsigned int i; for (i = 0; i < monitor_manager->n_crtcs; i++) { MetaCrtc *crtc = &monitor_manager->crtcs[i]; int x = crtc->rect.x - logical_monitor->rect.x; int y = crtc->rect.y - logical_monitor->rect.y; if (crtc->logical_monitor != logical_monitor) continue; meta_onscreen_native_flip_crtc (onscreen_native, flip_closure, crtc, x, y, &fb_in_use); } } else { unsigned int i; for (i = 0; i < monitor_manager->n_crtcs; i++) { MetaCrtc *crtc = &monitor_manager->crtcs[i]; meta_onscreen_native_flip_crtc (onscreen_native, flip_closure, crtc, crtc->rect.x, crtc->rect.y, &fb_in_use); } } /* * If the framebuffer is in use, but we don't have any pending flips it means * that flipping is not supported and we set the next framebuffer directly. * Since we won't receive a flip callback, lets just notify listeners * directly. */ if (fb_in_use && onscreen_native->pending_flips == 0) { MetaRenderer *renderer = meta_backend_get_renderer (backend); MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer); switch (renderer_native->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 } } onscreen_native->pending_queue_swap_notify = TRUE; g_closure_unref (flip_closure); } static gboolean gbm_get_next_fb_id (CoglOnscreen *onscreen, struct gbm_bo **out_next_bo, uint32_t *out_next_fb_id) { CoglContext *cogl_context = COGL_FRAMEBUFFER (onscreen)->context; CoglRenderer *cogl_renderer = cogl_context->display->renderer; CoglRendererEGL *egl_renderer = cogl_renderer->winsys; MetaRendererNative *renderer_native = egl_renderer->platform; CoglOnscreenEGL *egl_onscreen = onscreen->winsys; MetaOnscreenNative *onscreen_native = egl_onscreen->platform; uint32_t handle, stride; struct gbm_bo *next_bo; uint32_t next_fb_id; /* Now we need to set the CRTC to whatever is the front buffer */ next_bo = gbm_surface_lock_front_buffer (onscreen_native->gbm.surface); stride = gbm_bo_get_stride (next_bo); handle = gbm_bo_get_handle (next_bo).u32; if (drmModeAddFB (renderer_native->kms_fd, cogl_framebuffer_get_width (COGL_FRAMEBUFFER (onscreen)), cogl_framebuffer_get_height (COGL_FRAMEBUFFER (onscreen)), 24, /* depth */ 32, /* bpp */ stride, handle, &next_fb_id)) { g_warning ("Failed to create new back buffer handle: %m"); gbm_surface_release_buffer (onscreen_native->gbm.surface, next_bo); return FALSE; } *out_next_bo = next_bo; *out_next_fb_id = next_fb_id; return TRUE; } static void meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, const int *rectangles, int n_rectangles) { CoglContext *cogl_context = COGL_FRAMEBUFFER (onscreen)->context; CoglRenderer *cogl_renderer = cogl_context->display->renderer; CoglRendererEGL *egl_renderer = cogl_renderer->winsys; MetaRendererNative *renderer_native = egl_renderer->platform; CoglOnscreenEGL *egl_onscreen = onscreen->winsys; MetaOnscreenNative *onscreen_native = egl_onscreen->platform; MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerKms *monitor_manager_kms = META_MONITOR_MANAGER_KMS (monitor_manager); CoglFrameInfo *frame_info; frame_info = g_queue_peek_tail (&onscreen->pending_frame_infos); frame_info->global_frame_counter = renderer_native->frame_counter; /* * 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. */ while (onscreen_native->pending_flips) meta_monitor_manager_kms_wait_for_flip (monitor_manager_kms); parent_vtable->onscreen_swap_buffers_with_damage (onscreen, rectangles, n_rectangles); switch (renderer_native->mode) { case META_RENDERER_NATIVE_MODE_GBM: g_warn_if_fail (onscreen_native->gbm.next_bo == NULL && onscreen_native->gbm.next_fb_id == 0); if (!gbm_get_next_fb_id (onscreen, &onscreen_native->gbm.next_bo, &onscreen_native->gbm.next_fb_id)) return; break; #ifdef HAVE_EGL_DEVICE case META_RENDERER_NATIVE_MODE_EGL_DEVICE: break; #endif } /* If this is the first framebuffer to be presented then we now setup the * crtc modes, else we flip from the previous buffer */ if (onscreen_native->pending_set_crtc) { meta_onscreen_native_set_crtc_modes (onscreen_native); onscreen_native->pending_set_crtc = FALSE; } onscreen_native->pending_queue_swap_notify_frame_count = renderer_native->frame_counter; meta_onscreen_native_flip_crtcs (onscreen); } 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 *egl_renderer = cogl_renderer->winsys; MetaRendererNative *renderer_native = egl_renderer->platform; #endif 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); #ifdef HAVE_EGL_DEVICE if (renderer_native->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 meta_renderer_native_create_surface_gbm (MetaRendererNative *renderer_native, int width, int height, struct gbm_surface **gbm_surface, EGLSurface *egl_surface, 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); CoglDisplay *cogl_display = cogl_context->display; CoglDisplayEGL *egl_display = cogl_display->winsys; CoglRendererEGL *egl_renderer = cogl_display->renderer->winsys; struct gbm_surface *new_gbm_surface; EGLNativeWindowType egl_native_window; EGLSurface new_egl_surface; new_gbm_surface = gbm_surface_create (renderer_native->gbm.device, width, height, GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); 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 = eglCreateWindowSurface (egl_renderer->edpy, egl_display->egl_config, egl_native_window, NULL); if (new_egl_surface == EGL_NO_SURFACE) { gbm_surface_destroy (new_gbm_surface); g_set_error (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_ONSCREEN, "Failed to allocate 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 (MetaRendererNative *renderer_native, MetaLogicalMonitor *logical_monitor, int width, int height, EGLStreamKHR *out_egl_stream, EGLSurface *out_egl_surface, 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); CoglDisplay *cogl_display = cogl_context_get_display (cogl_context); CoglDisplayEGL *cogl_egl_display = cogl_display->winsys; CoglRenderer *cogl_renderer = cogl_display->renderer; CoglRendererEGL *egl_renderer = cogl_renderer->winsys; EGLDisplay egl_display = egl_renderer->edpy; MetaMonitor *monitor; MetaOutput *output; 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, 1, 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; monitor = meta_logical_monitor_get_monitors (logical_monitor)->data; output = meta_monitor_get_main_output (monitor); /* * An "logical_monitor" may have multiple outputs/crtcs in case its tiled, * but as far as I can tell, EGL only allows you to pass one crtc_id, so * lets pass the first one. */ output_attribs[0] = EGL_DRM_CRTC_EXT; output_attribs[1] = output->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_egl_display->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; } static gboolean init_dumb_fb (MetaRendererNative *renderer_native, MetaOnscreenNative *onscreen_native, int width, int height, 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; create_arg = (struct drm_mode_create_dumb) { .bpp = 32, /* RGBX8888 */ .width = width, .height = height }; if (drmIoctl (renderer_native->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; } if (!renderer_native->no_add_fb2) { uint32_t handles[4] = { create_arg.handle, }; uint32_t pitches[4] = { create_arg.pitch, }; uint32_t offsets[4] = { 0 }; if (drmModeAddFB2 (renderer_native->kms_fd, width, height, GBM_FORMAT_XRGB8888, handles, pitches, offsets, &fb_id, 0) != 0) { g_warning ("drmModeAddFB2 failed (%s), falling back to drmModeAddFB", g_strerror (errno)); renderer_native->no_add_fb2 = TRUE; } } if (renderer_native->no_add_fb2) { if (drmModeAddFB (renderer_native->kms_fd, width, height, 24 /* depth of RGBX8888 */, 32 /* bpp of RGBX8888 */, create_arg.pitch, create_arg.handle, &fb_id) != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "drmModeAddFB failed: %s", g_strerror (errno)); goto err_add_fb; } } map_arg = (struct drm_mode_map_dumb) { .handle = create_arg.handle }; if (drmIoctl (renderer_native->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, renderer_native->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; } onscreen_native->egl.dumb_fb.fb_id = fb_id; onscreen_native->egl.dumb_fb.handle = create_arg.handle; onscreen_native->egl.dumb_fb.map = map; onscreen_native->egl.dumb_fb.map_size = create_arg.size; return TRUE; err_mmap: err_map_dumb: drmModeRmFB (renderer_native->kms_fd, fb_id); err_add_fb: destroy_arg = (struct drm_mode_destroy_dumb) { .handle = create_arg.handle }; drmIoctl (renderer_native->kms_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg); err_ioctl: return FALSE; } static void release_dumb_fb (MetaRendererNative *renderer_native, MetaOnscreenNative *onscreen_native) { struct drm_mode_destroy_dumb destroy_arg; if (!onscreen_native->egl.dumb_fb.map) return; munmap (onscreen_native->egl.dumb_fb.map, onscreen_native->egl.dumb_fb.map_size); onscreen_native->egl.dumb_fb.map = NULL; drmModeRmFB (renderer_native->kms_fd, onscreen_native->egl.dumb_fb.fb_id); destroy_arg = (struct drm_mode_destroy_dumb) { .handle = onscreen_native->egl.dumb_fb.handle }; drmIoctl (renderer_native->kms_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg); } #endif /* HAVE_EGL_DEVICE */ 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 *egl_display = cogl_display->winsys; CoglOnscreenEGL *egl_onscreen; MetaOnscreenNative *onscreen_native; _COGL_RETURN_VAL_IF_FAIL (egl_display->egl_context, FALSE); onscreen->winsys = g_slice_new0 (CoglOnscreenEGL); egl_onscreen = onscreen->winsys; onscreen_native = g_slice_new0 (MetaOnscreenNative); egl_onscreen->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); CoglContext *cogl_context = framebuffer->context; CoglDisplay *cogl_display = cogl_context->display; CoglRenderer *cogl_renderer = cogl_display->renderer; CoglRendererEGL *egl_renderer = cogl_renderer->winsys; MetaRendererNative *renderer_native = egl_renderer->platform; CoglOnscreenEGL *egl_onscreen = onscreen->winsys; MetaOnscreenNative *onscreen_native = egl_onscreen->platform; struct gbm_surface *gbm_surface; EGLSurface egl_surface; int width; int height; #ifdef HAVE_EGL_DEVICE MetaRendererView *view; MetaLogicalMonitor *logical_monitor; 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; switch (renderer_native->mode) { case META_RENDERER_NATIVE_MODE_GBM: if (!meta_renderer_native_create_surface_gbm (renderer_native, width, height, &gbm_surface, &egl_surface, error)) return FALSE; onscreen_native->gbm.surface = gbm_surface; egl_onscreen->egl_surface = egl_surface; break; #ifdef HAVE_EGL_DEVICE case META_RENDERER_NATIVE_MODE_EGL_DEVICE: if (!init_dumb_fb (renderer_native, onscreen_native, width, height, error)) return FALSE; view = onscreen_native->view; logical_monitor = meta_renderer_view_get_logical_monitor (view); if (!meta_renderer_native_create_surface_egl_device (renderer_native, logical_monitor, width, height, &egl_stream, &egl_surface, error)) return FALSE; onscreen_native->egl.stream = egl_stream; egl_onscreen->egl_surface = egl_surface; break; #endif /* HAVE_EGL_DEVICE */ } return TRUE; } static void meta_renderer_native_release_onscreen (CoglOnscreen *onscreen) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *cogl_context = framebuffer->context; CoglRenderer *cogl_renderer = cogl_context->display->renderer; CoglRendererEGL *egl_renderer = cogl_renderer->winsys; CoglOnscreenEGL *egl_onscreen = onscreen->winsys; MetaRendererNative *renderer_native = egl_renderer->platform; MetaOnscreenNative *onscreen_native; /* If we never successfully allocated then there's nothing to do */ if (egl_onscreen == NULL) return; onscreen_native = egl_onscreen->platform; if (egl_onscreen->egl_surface != EGL_NO_SURFACE) { eglDestroySurface (egl_renderer->edpy, egl_onscreen->egl_surface); egl_onscreen->egl_surface = EGL_NO_SURFACE; } switch (renderer_native->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_id == 0); free_current_bo (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 (renderer_native, onscreen_native); if (onscreen_native->egl.stream != EGL_NO_STREAM_KHR) { MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); meta_egl_destroy_stream (egl, egl_renderer->edpy, onscreen_native->egl.stream, NULL); onscreen_native->egl.stream = EGL_NO_STREAM_KHR; } break; #endif /* HAVE_EGL_DEVICE */ } 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, .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 }; MetaRendererNativeMode meta_renderer_native_get_mode (MetaRendererNative *renderer_native) { return renderer_native->mode; } struct gbm_device * meta_renderer_native_get_gbm (MetaRendererNative *renderer_native) { return renderer_native->gbm.device; } int meta_renderer_native_get_kms_fd (MetaRendererNative *renderer_native) { return renderer_native->kms_fd; } 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 *egl_onscreen = onscreen->winsys; MetaOnscreenNative *onscreen_native = egl_onscreen->platform; onscreen_native->pending_set_crtc = TRUE; } renderer_native->pending_unset_disabled_crtcs = TRUE; } static CoglOnscreen * meta_renderer_native_create_onscreen (MetaRendererNative *renderer, CoglContext *context, MetaMonitorTransform transform, gint view_width, gint view_height) { CoglOnscreen *onscreen; gint width, height; GError *error = NULL; if (meta_monitor_transform_is_rotated (transform)) { width = view_height; height = view_width; } else { width = view_width; height = view_height; } onscreen = cogl_onscreen_new (context, width, height); cogl_onscreen_set_swap_throttled (onscreen, _clutter_get_sync_to_vblank ()); if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (onscreen), &error)) { g_warning ("Could not create onscreen: %s", error->message); cogl_object_unref (onscreen); g_error_free (error); return NULL; } return onscreen; } static CoglOffscreen * meta_renderer_native_create_offscreen (MetaRendererNative *renderer, CoglContext *context, MetaMonitorTransform transform, gint view_width, gint view_height) { CoglOffscreen *fb; CoglTexture2D *tex; GError *error = NULL; 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)) { g_warning ("Could not create offscreen: %s", error->message); g_error_free (error); cogl_object_unref (fb); return FALSE; } return fb; } gboolean meta_renderer_native_set_legacy_view_size (MetaRendererNative *renderer_native, MetaRendererView *view, int width, int height, GError **error) { ClutterBackend *clutter_backend = clutter_get_default_backend (); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); CoglDisplay *cogl_display = cogl_context_get_display (cogl_context); CoglRendererEGL *egl_renderer = cogl_display->renderer->winsys; ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view); cairo_rectangle_int_t view_layout; clutter_stage_view_get_layout (stage_view, &view_layout); if (width != view_layout.width || height != view_layout.height) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerKms *monitor_manager_kms = META_MONITOR_MANAGER_KMS (monitor_manager); CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (stage_view); CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreenEGL *egl_onscreen = onscreen->winsys; MetaOnscreenNative *onscreen_native = egl_onscreen->platform; CoglDisplayEGL *egl_display = cogl_display->winsys; struct gbm_surface *new_surface; EGLSurface new_egl_surface; cairo_rectangle_int_t view_layout; /* * Ensure we don't have any pending flips that will want * to swap the current buffer. */ while (onscreen_native->gbm.next_fb_id != 0) meta_monitor_manager_kms_wait_for_flip (monitor_manager_kms); /* Need to drop the GBM surface and create a new one */ if (!meta_renderer_native_create_surface_gbm (renderer_native, width, height, &new_surface, &new_egl_surface, error)) return FALSE; if (egl_onscreen->egl_surface) { _cogl_winsys_egl_make_current (cogl_display, egl_display->dummy_surface, egl_display->dummy_surface, egl_display->egl_context); eglDestroySurface (egl_renderer->edpy, egl_onscreen->egl_surface); } /* * Release the current buffer and destroy the associated surface. The * kernel will deal with keeping the actual buffer alive until its no * longer used. */ free_current_bo (onscreen); g_clear_pointer (&onscreen_native->gbm.surface, gbm_surface_destroy); /* * Update the active gbm and egl surfaces and make sure they they are * used for drawing the coming frame. */ onscreen_native->gbm.surface = new_surface; egl_onscreen->egl_surface = new_egl_surface; _cogl_winsys_egl_make_current (cogl_display, egl_onscreen->egl_surface, egl_onscreen->egl_surface, egl_display->egl_context); view_layout = (cairo_rectangle_int_t) { .width = width, .height = height }; g_object_set (G_OBJECT (view), "layout", &view_layout, NULL); _cogl_framebuffer_winsys_update_size (framebuffer, width, height); } meta_renderer_native_queue_modes_reset (renderer_native); return TRUE; } 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.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_inited = TRUE; } return &vtable; } static CoglRenderer * meta_renderer_native_create_cogl_renderer (MetaRenderer *renderer) { CoglRenderer *cogl_renderer; cogl_renderer = cogl_renderer_new (); cogl_renderer_set_custom_winsys (cogl_renderer, get_native_cogl_winsys_vtable); return cogl_renderer; } static void meta_onscreen_native_set_view (CoglOnscreen *onscreen, MetaRendererView *view) { CoglOnscreenEGL *egl_onscreen; MetaOnscreenNative *onscreen_native; egl_onscreen = onscreen->winsys; onscreen_native = egl_onscreen->platform; onscreen_native->view = view; } MetaRendererView * meta_renderer_native_create_legacy_view (MetaRendererNative *renderer_native) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); CoglOnscreen *onscreen = NULL; 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 = { 0 }; MetaRendererView *view; GError *error = NULL; if (!monitor_manager) return NULL; meta_monitor_manager_get_screen_size (monitor_manager, &view_layout.width, &view_layout.height); onscreen = meta_renderer_native_create_onscreen (renderer_native, cogl_context, META_MONITOR_TRANSFORM_NORMAL, view_layout.width, view_layout.height); if (!onscreen) meta_fatal ("Failed to allocate onscreen framebuffer\n"); view = g_object_new (META_TYPE_RENDERER_VIEW, "layout", &view_layout, "framebuffer", onscreen, NULL); 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); return view; } static MetaMonitorTransform calculate_view_transform (MetaMonitorManager *monitor_manager, MetaLogicalMonitor *logical_monitor) { MetaMonitor *main_monitor; MetaOutput *main_output; main_monitor = meta_logical_monitor_get_monitors (logical_monitor)->data; main_output = meta_monitor_get_main_output (main_monitor); /* * 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, main_output->crtc, logical_monitor->transform)) return META_MONITOR_TRANSFORM_NORMAL; else return logical_monitor->transform; } static MetaRendererView * meta_renderer_native_create_view (MetaRenderer *renderer, MetaLogicalMonitor *logical_monitor) { 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); CoglDisplay *cogl_display = cogl_context_get_display (cogl_context); CoglDisplayEGL *egl_display = cogl_display->winsys; CoglOnscreenEGL *egl_onscreen; MetaMonitorTransform view_transform; CoglOnscreen *onscreen = NULL; CoglOffscreen *offscreen = NULL; float scale; int width, height; MetaRendererView *view; GError *error = NULL; view_transform = calculate_view_transform (monitor_manager, logical_monitor); if (meta_is_stage_views_scaled ()) scale = meta_logical_monitor_get_scale (logical_monitor); else scale = 1.0; width = roundf (logical_monitor->rect.width * scale); height = roundf (logical_monitor->rect.height * scale); onscreen = meta_renderer_native_create_onscreen (META_RENDERER_NATIVE (renderer), cogl_context, view_transform, width, height); if (!onscreen) meta_fatal ("Failed to allocate onscreen framebuffer\n"); if (view_transform != META_MONITOR_TRANSFORM_NORMAL) { offscreen = meta_renderer_native_create_offscreen (META_RENDERER_NATIVE (renderer), cogl_context, view_transform, width, height); if (!offscreen) meta_fatal ("Failed to allocate back buffer texture\n"); } view = g_object_new (META_TYPE_RENDERER_VIEW, "layout", &logical_monitor->rect, "scale", scale, "framebuffer", onscreen, "offscreen", offscreen, "logical-monitor", logical_monitor, "transform", view_transform, NULL); g_clear_pointer (&offscreen, 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 */ egl_onscreen = onscreen->winsys; _cogl_winsys_egl_make_current (cogl_display, egl_onscreen->egl_surface, egl_onscreen->egl_surface, egl_display->egl_context); return view; } void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native) { renderer_native->frame_counter++; if (renderer_native->pending_unset_disabled_crtcs) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerKms *monitor_manager_kms = META_MONITOR_MANAGER_KMS (monitor_manager); unsigned int i; for (i = 0; i < monitor_manager->n_crtcs; i++) { MetaCrtc *crtc = &monitor_manager->crtcs[i]; if (crtc->current_mode) continue; meta_monitor_manager_kms_apply_crtc_mode (monitor_manager_kms, crtc, 0, 0, 0); } renderer_native->pending_unset_disabled_crtcs = FALSE; } } int64_t meta_renderer_native_get_frame_counter (MetaRendererNative *renderer_native) { return renderer_native->frame_counter; } static void meta_renderer_native_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaRendererNative *renderer_native = META_RENDERER_NATIVE (object); switch (prop_id) { case PROP_KMS_FD: g_value_set_int (value, renderer_native->kms_fd); break; case PROP_KMS_FILE_PATH: g_value_set_string (value, renderer_native->kms_file_path); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_renderer_native_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaRendererNative *renderer_native = META_RENDERER_NATIVE (object); switch (prop_id) { case PROP_KMS_FD: renderer_native->kms_fd = g_value_get_int (value); break; case PROP_KMS_FILE_PATH: renderer_native->kms_file_path = g_strdup (g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_renderer_native_finalize (GObject *object) { MetaRendererNative *renderer_native = META_RENDERER_NATIVE (object); g_clear_pointer (&renderer_native->gbm.device, gbm_device_destroy); g_free (renderer_native->kms_file_path); G_OBJECT_CLASS (meta_renderer_native_parent_class)->finalize (object); } static gboolean init_gbm (MetaRendererNative *renderer_native, GError **error) { MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); struct gbm_device *gbm_device; EGLDisplay egl_display; if (!meta_egl_has_extensions (egl, EGL_NO_DISPLAY, NULL, "EGL_MESA_platform_gbm", NULL)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Missing extension for GBM renderer: EGL_KHR_platform_gbm"); return FALSE; } gbm_device = gbm_create_device (renderer_native->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 FALSE; } egl_display = meta_egl_get_platform_display (egl, EGL_PLATFORM_GBM_KHR, gbm_device, NULL, error); if (egl_display == EGL_NO_DISPLAY) { gbm_device_destroy (gbm_device); return FALSE; } renderer_native->egl_display = egl_display; renderer_native->gbm.device = gbm_device; renderer_native->mode = META_RENDERER_NATIVE_MODE_GBM; return TRUE; } #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, GError **error) { MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); char **missing_extensions; EGLint num_devices; EGLDeviceEXT *devices; 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 (", ", 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; } 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, renderer_native->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, EGLDeviceEXT egl_device, GError **error) { MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); EGLint platform_attribs[] = { EGL_DRM_MASTER_FD_EXT, renderer_native->kms_fd, EGL_NONE }; return meta_egl_get_platform_display (egl, EGL_PLATFORM_DEVICE_EXT, (void *) egl_device, platform_attribs, error); } static gboolean init_egl_device (MetaRendererNative *renderer_native, GError **error) { MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); char **missing_extensions; EGLDeviceEXT egl_device; EGLDisplay egl_display; if (!meta_is_stage_views_enabled()) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "EGLDevice requires stage views enabled"); return FALSE; } egl_device = find_egl_device (renderer_native, error); if (egl_device == EGL_NO_DEVICE_EXT) return FALSE; egl_display = get_egl_device_display (renderer_native, egl_device, error); if (egl_display == EGL_NO_DISPLAY) return FALSE; if (!meta_egl_initialize (egl, egl_display, error)) return FALSE; 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 (", ", 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 FALSE; } renderer_native->egl_display = egl_display; renderer_native->egl.device = egl_device; renderer_native->mode = META_RENDERER_NATIVE_MODE_EGL_DEVICE; return TRUE; } #endif /* HAVE_EGL_DEVICE */ static gboolean meta_renderer_native_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { MetaRendererNative *renderer_native = META_RENDERER_NATIVE (initable); 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) */ if (init_egl_device (renderer_native, &egl_device_error)) return TRUE; #endif if (init_gbm (renderer_native, &gbm_error)) { #ifdef HAVE_EGL_DEVICE g_error_free (egl_device_error); #endif return TRUE; } 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 FALSE; } static void initable_iface_init (GInitableIface *initable_iface) { initable_iface->init = meta_renderer_native_initable_init; } static void meta_renderer_native_init (MetaRendererNative *renderer_native) { } 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->get_property = meta_renderer_native_get_property; object_class->set_property = meta_renderer_native_set_property; object_class->finalize = meta_renderer_native_finalize; renderer_class->create_cogl_renderer = meta_renderer_native_create_cogl_renderer; renderer_class->create_view = meta_renderer_native_create_view; g_object_class_install_property (object_class, PROP_KMS_FD, g_param_spec_int ("kms-fd", "KMS fd", "The KMS file descriptor", 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_KMS_FILE_PATH, g_param_spec_string ("kms-file-path", "KMS file path", "The KMS file path", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } MetaRendererNative * meta_renderer_native_new (int kms_fd, const char *kms_file_path, GError **error) { MetaRendererNative *renderer_native; renderer_native = g_object_new (META_TYPE_RENDERER_NATIVE, "kms-fd", kms_fd, "kms-file-path", kms_file_path, NULL); if (!g_initable_init (G_INITABLE (renderer_native), NULL, error)) { g_object_unref (renderer_native); return NULL; } return renderer_native; } ukwm/src/backends/native/meta-idle-monitor-native.c0000664000175000017500000001325613220600404021266 0ustar fengfeng/* -*- 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 "meta-idle-monitor-native.h" #include "meta-idle-monitor-private.h" #include #include "display-private.h" #include struct _MetaIdleMonitorNative { MetaIdleMonitor parent; guint64 last_event_time; }; struct _MetaIdleMonitorNativeClass { MetaIdleMonitorClass parent_class; }; typedef struct { MetaIdleMonitorWatch base; GSource *timeout_source; } MetaIdleMonitorWatchNative; G_DEFINE_TYPE (MetaIdleMonitorNative, meta_idle_monitor_native, META_TYPE_IDLE_MONITOR) static gint64 meta_idle_monitor_native_get_idletime (MetaIdleMonitor *monitor) { MetaIdleMonitorNative *monitor_native = META_IDLE_MONITOR_NATIVE (monitor); return (g_get_monotonic_time () - monitor_native->last_event_time) / 1000; } static guint32 get_next_watch_serial (void) { static guint32 serial = 0; g_atomic_int_inc (&serial); return serial; } static gboolean native_dispatch_timeout (GSource *source, GSourceFunc callback, gpointer user_data) { MetaIdleMonitorWatchNative *watch_native = user_data; MetaIdleMonitorWatch *watch = (MetaIdleMonitorWatch *) watch_native; _meta_idle_monitor_watch_fire (watch); g_source_set_ready_time (watch_native->timeout_source, -1); return TRUE; } static GSourceFuncs native_source_funcs = { NULL, /* prepare */ NULL, /* check */ native_dispatch_timeout, NULL, /* finalize */ }; static void free_watch (gpointer data) { MetaIdleMonitorWatchNative *watch_native = data; MetaIdleMonitorWatch *watch = (MetaIdleMonitorWatch *) watch_native; MetaIdleMonitor *monitor = watch->monitor; g_object_ref (monitor); if (watch->idle_source_id) { g_source_remove (watch->idle_source_id); watch->idle_source_id = 0; } if (watch->notify != NULL) watch->notify (watch->user_data); if (watch_native->timeout_source != NULL) g_source_destroy (watch_native->timeout_source); g_object_unref (monitor); g_slice_free (MetaIdleMonitorWatchNative, watch_native); } static MetaIdleMonitorWatch * meta_idle_monitor_native_make_watch (MetaIdleMonitor *monitor, guint64 timeout_msec, MetaIdleMonitorWatchFunc callback, gpointer user_data, GDestroyNotify notify) { MetaIdleMonitorWatchNative *watch_native; MetaIdleMonitorWatch *watch; MetaIdleMonitorNative *monitor_native = META_IDLE_MONITOR_NATIVE (monitor); watch_native = g_slice_new0 (MetaIdleMonitorWatchNative); watch = (MetaIdleMonitorWatch *) watch_native; 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 (&native_source_funcs, sizeof (GSource)); g_source_set_callback (source, NULL, watch, NULL); g_source_set_ready_time (source, monitor_native->last_event_time + timeout_msec * 1000); g_source_attach (source, NULL); g_source_unref (source); watch_native->timeout_source = source; } return watch; } static void meta_idle_monitor_native_class_init (MetaIdleMonitorNativeClass *klass) { MetaIdleMonitorClass *idle_monitor_class = META_IDLE_MONITOR_CLASS (klass); idle_monitor_class->get_idletime = meta_idle_monitor_native_get_idletime; idle_monitor_class->make_watch = meta_idle_monitor_native_make_watch; } static void meta_idle_monitor_native_init (MetaIdleMonitorNative *monitor_native) { MetaIdleMonitor *monitor = META_IDLE_MONITOR (monitor_native); monitor->watches = g_hash_table_new_full (NULL, NULL, NULL, free_watch); monitor_native->last_event_time = g_get_monotonic_time (); } void meta_idle_monitor_native_reset_idletime (MetaIdleMonitor *monitor) { MetaIdleMonitorNative *monitor_native = META_IDLE_MONITOR_NATIVE (monitor); GList *node, *watch_ids; monitor_native->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); MetaIdleMonitorWatchNative *watch; watch = g_hash_table_lookup (monitor->watches, GUINT_TO_POINTER (watch_id)); if (!watch) continue; if (watch->base.timeout_msec == 0) { _meta_idle_monitor_watch_fire ((MetaIdleMonitorWatch *) watch); } else { g_source_set_ready_time (watch->timeout_source, monitor_native->last_event_time + watch->base.timeout_msec * 1000); } } g_list_free (watch_ids); } ukwm/src/backends/native/meta-input-settings-native.h0000664000175000017500000000410413220600404021656 0ustar fengfeng/* -*- 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 "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 */ ukwm/src/backends/native/meta-backend-native-private.h0000664000175000017500000000220113233511035021721 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 */ ukwm/src/backends/native/meta-barrier-native.c0000664000175000017500000004320013233511035020307 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ /** * SECTION:barrier-native * @Title: MetaBarrierImplNative * @Short_Description: Pointer barriers implementation for the native backend */ #include "config.h" #include #include #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 "backends/native/meta-barrier-native.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 _MetaBarrierImplNativePrivate { MetaBarrier *barrier; MetaBarrierManagerNative *manager; gboolean is_active; MetaBarrierState state; int trigger_serial; guint32 last_event_time; MetaBarrierDirection blocked_dir; }; G_DEFINE_TYPE_WITH_PRIVATE (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) { MetaBarrierImplNativePrivate *priv = meta_barrier_impl_native_get_instance_private (self); priv->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; MetaBarrierImplNativePrivate *priv = meta_barrier_impl_native_get_instance_private (self); MetaBarrier *barrier = priv->barrier; MetaLine2 *motion = user_data; MetaLine2 hit_box; if (priv->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; MetaBarrierImplNativePrivate *priv = meta_barrier_impl_native_get_instance_private (self); MetaBarrier *barrier = priv->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 (priv->state == META_BARRIER_STATE_RELEASE) return; /* Ignore if we are moving away from barrier. */ if (priv->state == META_BARRIER_STATE_HELD && (data->in.directions & priv->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) { MetaBarrierImplNativePrivate *priv = meta_barrier_impl_native_get_instance_private (self); MetaBarrier *barrier = priv->barrier; MetaBarrierEvent *event = g_slice_new0 (MetaBarrierEvent); MetaBarrierState old_state = priv->state; switch (priv->state) { case META_BARRIER_STATE_HIT: priv->state = META_BARRIER_STATE_HELD; priv->trigger_serial = next_serial (); event->dt = 0; break; case META_BARRIER_STATE_RELEASE: case META_BARRIER_STATE_LEFT: priv->state = META_BARRIER_STATE_ACTIVE; /* Intentional fall-through. */ case META_BARRIER_STATE_HELD: event->dt = time - priv->last_event_time; break; case META_BARRIER_STATE_ACTIVE: g_assert_not_reached (); /* Invalid state. */ } event->ref_count = 1; event->event_id = priv->trigger_serial; event->time = time; event->x = x; event->y = y; event->dx = dx; event->dy = dy; event->grabbed = priv->state == META_BARRIER_STATE_HELD; event->released = old_state == META_BARRIER_STATE_RELEASE; priv->last_event_time = time; if (priv->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; MetaBarrierImplNativePrivate *priv = meta_barrier_impl_native_get_instance_private (self); MetaBarrierEventData *data = user_data; switch (priv->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) { MetaBarrierImplNativePrivate *priv = meta_barrier_impl_native_get_instance_private (self); MetaBarrier *barrier = priv->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; priv->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; priv->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); } priv->state = META_BARRIER_STATE_HIT; } void meta_barrier_manager_native_process (MetaBarrierManagerNative *manager, ClutterInputDevice *device, guint32 time, float *x, float *y) { ClutterPoint 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); MetaBarrierImplNativePrivate *priv = meta_barrier_impl_native_get_instance_private (self); return priv->is_active; } static void _meta_barrier_impl_native_release (MetaBarrierImpl *impl, MetaBarrierEvent *event) { MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl); MetaBarrierImplNativePrivate *priv = meta_barrier_impl_native_get_instance_private (self); if (priv->state == META_BARRIER_STATE_HELD && event->event_id == priv->trigger_serial) priv->state = META_BARRIER_STATE_RELEASE; } static void _meta_barrier_impl_native_destroy (MetaBarrierImpl *impl) { MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl); MetaBarrierImplNativePrivate *priv = meta_barrier_impl_native_get_instance_private (self); g_hash_table_remove (priv->manager->barriers, self); priv->is_active = FALSE; } MetaBarrierImpl * meta_barrier_impl_native_new (MetaBarrier *barrier) { MetaBarrierImplNative *self; MetaBarrierImplNativePrivate *priv; MetaBackendNative *native; MetaBarrierManagerNative *manager; self = g_object_new (META_TYPE_BARRIER_IMPL_NATIVE, NULL); priv = meta_barrier_impl_native_get_instance_private (self); priv->barrier = barrier; priv->is_active = TRUE; native = META_BACKEND_NATIVE (meta_get_backend ()); manager = meta_backend_native_get_barrier_manager (native); priv->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; } ukwm/src/backends/native/meta-default-modes.h0000664000175000017500000000755213220600404020140 0ustar fengfeng/* Generated by gen-default-modes.py */ const drmModeModeInfo meta_default_drm_mode_infos[] = { { 38250, 800, 832, 912, 1024, 0, 600, 603, 607, 624, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "800x600_60.00" }, { 63500, 1024, 1072, 1176, 1328, 0, 768, 771, 775, 798, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "1024x768_60.00" }, { 81750, 1152, 1216, 1336, 1520, 0, 864, 867, 871, 897, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "1152x864_60.00" }, { 101250, 1280, 1360, 1488, 1696, 0, 960, 963, 967, 996, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "1280x960_60.00" }, { 121750, 1400, 1488, 1632, 1864, 0, 1050, 1053, 1057, 1089, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "1400x1050_60.00" }, { 129000, 1440, 1528, 1680, 1920, 0, 1080, 1083, 1087, 1120, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "1440x1080_60.00" }, { 161000, 1600, 1712, 1880, 2160, 0, 1200, 1203, 1207, 1245, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "1600x1200_60.00" }, { 233500, 1920, 2064, 2264, 2608, 0, 1440, 1443, 1447, 1493, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "1920x1440_60.00" }, { 267250, 2048, 2208, 2424, 2800, 0, 1536, 1539, 1543, 1592, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "2048x1536_60.00" }, { 83500, 1280, 1352, 1480, 1680, 0, 800, 803, 809, 831, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "1280x800_60.00" }, { 106500, 1440, 1528, 1672, 1904, 0, 900, 903, 909, 934, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "1440x900_60.00" }, { 146250, 1680, 1784, 1960, 2240, 0, 1050, 1053, 1059, 1089, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "1680x1050_60.00" }, { 193250, 1920, 2056, 2256, 2592, 0, 1200, 1203, 1209, 1245, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "1920x1200_60.00" }, { 348500, 2560, 2760, 3032, 3504, 0, 1600, 1603, 1609, 1658, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "2560x1600_60.00" }, { 74500, 1280, 1344, 1472, 1664, 0, 720, 723, 728, 748, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "1280x720_60.00" }, { 85250, 1368, 1440, 1576, 1784, 0, 768, 771, 781, 798, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "1368x768_60.00" }, { 118250, 1600, 1696, 1856, 2112, 0, 900, 903, 908, 934, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "1600x900_60.00" }, { 173000, 1920, 2048, 2248, 2576, 0, 1080, 1083, 1088, 1120, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "1920x1080_60.00" }, { 197000, 2048, 2184, 2400, 2752, 0, 1152, 1155, 1160, 1195, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "2048x1152_60.00" }, { 312250, 2560, 2752, 3024, 3488, 0, 1440, 1443, 1448, 1493, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "2560x1440_60.00" }, { 396250, 2880, 3096, 3408, 3936, 0, 1620, 1623, 1628, 1679, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "2880x1620_60.00" }, { 492000, 3200, 3456, 3800, 4400, 0, 1800, 1803, 1808, 1865, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "3200x1800_60.00" }, { 712750, 3840, 4160, 4576, 5312, 0, 2160, 2163, 2168, 2237, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "3840x2160_60.00" }, { 813000, 4096, 4440, 4888, 5680, 0, 2304, 2307, 2312, 2386, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "4096x2304_60.00" }, { 1276500, 5120, 5560, 6128, 7136, 0, 2880, 2883, 2888, 2982, 0, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, DRM_MODE_TYPE_DEFAULT, "5120x2880_60.00" }, }; ukwm/src/backends/native/dbus-utils.h0000664000175000017500000000205013233511035016547 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 */ ukwm/src/backends/native/meta-renderer-native.h0000664000175000017500000000552113233511035020500 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #ifndef META_RENDERER_NATIVE_H #define META_RENDERER_NATIVE_H #include #include #include "backends/meta-renderer.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 (int kms_fd, const char *kms_file_path, GError **error); MetaRendererNativeMode meta_renderer_native_get_mode (MetaRendererNative *renderer_native); struct gbm_device * meta_renderer_native_get_gbm (MetaRendererNative *renderer_native); int meta_renderer_native_get_kms_fd (MetaRendererNative *renderer_native); void meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native); gboolean meta_renderer_native_set_legacy_view_size (MetaRendererNative *renderer_native, MetaRendererView *view, int width, int height, GError **error); void meta_renderer_native_set_ignore_crtc (MetaRendererNative *renderer_native, uint32_t id, gboolean ignore); MetaRendererView * meta_renderer_native_create_legacy_view (MetaRendererNative *renderer_native); void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native); int64_t meta_renderer_native_get_frame_counter (MetaRendererNative *renderer_native); #endif /* META_RENDERER_NATIVE_H */ ukwm/src/backends/native/meta-cursor-renderer-native.h0000664000175000017500000000474513233511035022022 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_CURSOR_RENDERER_NATIVE_H #define META_CURSOR_RENDERER_NATIVE_H #include "meta-cursor-renderer.h" #define META_TYPE_CURSOR_RENDERER_NATIVE (meta_cursor_renderer_native_get_type ()) #define META_CURSOR_RENDERER_NATIVE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_CURSOR_RENDERER_NATIVE, MetaCursorRendererNative)) #define META_CURSOR_RENDERER_NATIVE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_CURSOR_RENDERER_NATIVE, MetaCursorRendererNativeClass)) #define META_IS_CURSOR_RENDERER_NATIVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_CURSOR_RENDERER_NATIVE)) #define META_IS_CURSOR_RENDERER_NATIVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_CURSOR_RENDERER_NATIVE)) #define META_CURSOR_RENDERER_NATIVE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_CURSOR_RENDERER_NATIVE, MetaCursorRendererNativeClass)) typedef struct _MetaCursorRendererNative MetaCursorRendererNative; typedef struct _MetaCursorRendererNativeClass MetaCursorRendererNativeClass; struct _MetaCursorRendererNative { MetaCursorRenderer parent; }; struct _MetaCursorRendererNativeClass { MetaCursorRendererClass parent_class; }; GType meta_cursor_renderer_native_get_type (void) G_GNUC_CONST; struct gbm_device * meta_cursor_renderer_native_get_gbm_device (MetaCursorRendererNative *renderer); void meta_cursor_renderer_native_get_cursor_size (MetaCursorRendererNative *native, uint64_t *width, uint64_t *height); void meta_cursor_renderer_native_force_update (MetaCursorRendererNative *renderer); #endif /* META_CURSOR_RENDERER_NATIVE_H */ ukwm/src/backends/native/gen-default-modes.py0000664000175000017500000000457613233511035020174 0ustar fengfeng# 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., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. import os import sys 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", "const drmModeModeInfo meta_default_drm_mode_infos[] = {", ] def sync_flags(hsync, vsync): flags = "DRM_MODE_FLAG_" flags += "NHSYNC" if hsync[0] is '-' else "PHSYNC" flags += " | DRM_MODE_FLAG_" flags += "NVSYNC" if vsync[0] is '-' 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]) 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("};") for line in output_lines: sys.stdout.write(line + "\n") sys.stdout.flush() ukwm/src/backends/native/meta-monitor-manager-kms.c0000664000175000017500000015755113233511035021303 0ustar fengfeng/* -*- 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author: Giovanni Campagna */ #include "config.h" #include "meta-monitor-manager-kms.h" #include "meta-monitor-config-manager.h" #include "meta-backend-private.h" #include "meta-renderer-native.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "meta-default-modes.h" #define ALL_TRANSFORMS (META_MONITOR_TRANSFORM_FLIPPED_270 + 1) #define ALL_TRANSFORMS_MASK ((1 << ALL_TRANSFORMS) - 1) #define SYNC_TOLERANCE 0.01 /* 1 percent */ typedef struct { drmModeConnector *connector; unsigned n_encoders; drmModeEncoderPtr *encoders; drmModeEncoderPtr current_encoder; /* * Bitmasks of encoder position in the resources array (used during clone * setup). */ uint32_t encoder_mask; uint32_t enc_clone_mask; uint32_t dpms_prop_id; uint32_t edid_blob_id; uint32_t tile_blob_id; int suggested_x; int suggested_y; uint32_t hotplug_mode_update; gboolean has_scaling; } MetaOutputKms; typedef struct { uint32_t underscan_prop_id; uint32_t underscan_hborder_prop_id; uint32_t underscan_vborder_prop_id; uint32_t primary_plane_id; uint32_t rotation_prop_id; uint32_t rotation_map[ALL_TRANSFORMS]; uint32_t all_hw_transforms; } MetaCrtcKms; typedef struct { GSource source; gpointer fd_tag; MetaMonitorManagerKms *manager_kms; } MetaKmsSource; struct _MetaMonitorManagerKms { MetaMonitorManager parent_instance; int fd; MetaKmsSource *source; drmModeConnector **connectors; unsigned int n_connectors; GUdevClient *udev; guint uevent_handler_id; gboolean page_flips_not_supported; int max_buffer_width; int max_buffer_height; }; struct _MetaMonitorManagerKmsClass { MetaMonitorManagerClass parent_class; }; G_DEFINE_TYPE (MetaMonitorManagerKms, meta_monitor_manager_kms, META_TYPE_MONITOR_MANAGER); static void free_resources (MetaMonitorManagerKms *manager_kms) { unsigned i; for (i = 0; i < manager_kms->n_connectors; i++) drmModeFreeConnector (manager_kms->connectors[i]); g_free (manager_kms->connectors); } 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 * make_output_name (drmModeConnector *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 (connector->connector_type < G_N_ELEMENTS (connector_type_names)) return g_strdup_printf ("%s-%d", connector_type_names[connector->connector_type], connector->connector_type_id); else return g_strdup_printf ("Unknown%d-%d", connector->connector_type, connector->connector_type_id); } static void meta_output_destroy_notify (MetaOutput *output) { MetaOutputKms *output_kms; unsigned i; output_kms = output->driver_private; for (i = 0; i < output_kms->n_encoders; i++) drmModeFreeEncoder (output_kms->encoders[i]); g_free (output_kms->encoders); g_slice_free (MetaOutputKms, output_kms); } static void meta_monitor_mode_destroy_notify (MetaCrtcMode *mode) { g_slice_free (drmModeModeInfo, mode->driver_private); } static void meta_crtc_destroy_notify (MetaCrtc *crtc) { g_free (crtc->driver_private); } static gboolean drm_mode_equal (gconstpointer one, gconstpointer two) { const drmModeModeInfo *m_one = one; const drmModeModeInfo *m_two = two; return m_one->clock == m_two->clock && m_one->hdisplay == m_two->hdisplay && m_one->hsync_start == m_two->hsync_start && m_one->hsync_end == m_two->hsync_end && m_one->htotal == m_two->htotal && m_one->hskew == m_two->hskew && m_one->vdisplay == m_two->vdisplay && m_one->vsync_start == m_two->vsync_start && m_one->vsync_end == m_two->vsync_end && m_one->vtotal == m_two->vtotal && m_one->vscan == m_two->vscan && m_one->vrefresh == m_two->vrefresh && m_one->flags == m_two->flags && m_one->type == m_two->type && strncmp (m_one->name, m_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; } static void find_connector_properties (MetaMonitorManagerKms *manager_kms, MetaOutputKms *output_kms) { int i; output_kms->hotplug_mode_update = 0; output_kms->suggested_x = -1; output_kms->suggested_y = -1; for (i = 0; i < output_kms->connector->count_props; i++) { drmModePropertyPtr prop = drmModeGetProperty (manager_kms->fd, output_kms->connector->props[i]); if (!prop) continue; if ((prop->flags & DRM_MODE_PROP_ENUM) && strcmp (prop->name, "DPMS") == 0) output_kms->dpms_prop_id = prop->prop_id; else if ((prop->flags & DRM_MODE_PROP_BLOB) && strcmp (prop->name, "EDID") == 0) output_kms->edid_blob_id = output_kms->connector->prop_values[i]; else if ((prop->flags & DRM_MODE_PROP_BLOB) && strcmp (prop->name, "TILE") == 0) output_kms->tile_blob_id = output_kms->connector->prop_values[i]; else if ((prop->flags & DRM_MODE_PROP_RANGE) && strcmp (prop->name, "suggested X") == 0) output_kms->suggested_x = output_kms->connector->prop_values[i]; else if ((prop->flags & DRM_MODE_PROP_RANGE) && strcmp (prop->name, "suggested Y") == 0) output_kms->suggested_y = output_kms->connector->prop_values[i]; else if ((prop->flags & DRM_MODE_PROP_RANGE) && strcmp (prop->name, "hotplug_mode_update") == 0) output_kms->hotplug_mode_update = output_kms->connector->prop_values[i]; else if (strcmp (prop->name, "scaling mode") == 0) output_kms->has_scaling = TRUE; drmModeFreeProperty (prop); } } static void find_crtc_properties (MetaMonitorManagerKms *manager_kms, MetaCrtc *meta_crtc) { MetaCrtcKms *crtc_kms; drmModeObjectPropertiesPtr props; size_t i; crtc_kms = meta_crtc->driver_private; props = drmModeObjectGetProperties (manager_kms->fd, meta_crtc->crtc_id, DRM_MODE_OBJECT_CRTC); if (!props) return; for (i = 0; i < props->count_props; i++) { drmModePropertyPtr prop = drmModeGetProperty (manager_kms->fd, props->props[i]); if (!prop) continue; if ((prop->flags & DRM_MODE_PROP_ENUM) && strcmp (prop->name, "underscan") == 0) crtc_kms->underscan_prop_id = prop->prop_id; else if ((prop->flags & DRM_MODE_PROP_RANGE) && strcmp (prop->name, "underscan hborder") == 0) crtc_kms->underscan_hborder_prop_id = prop->prop_id; else if ((prop->flags & DRM_MODE_PROP_RANGE) && strcmp (prop->name, "underscan vborder") == 0) crtc_kms->underscan_vborder_prop_id = prop->prop_id; drmModeFreeProperty (prop); } } static drmModePropertyBlobPtr read_edid_blob (MetaMonitorManagerKms *manager_kms, uint32_t edid_blob_id, GError **error) { drmModePropertyBlobPtr edid_blob = NULL; edid_blob = drmModeGetPropertyBlob (manager_kms->fd, edid_blob_id); if (!edid_blob) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "%s", strerror (errno)); return NULL; } return edid_blob; } static GBytes * read_output_edid (MetaMonitorManagerKms *manager_kms, MetaOutput *output, GError **error) { MetaOutputKms *output_kms = output->driver_private; drmModePropertyBlobPtr edid_blob; g_assert (output_kms->edid_blob_id != 0); edid_blob = read_edid_blob (manager_kms, output_kms->edid_blob_id, error); if (!edid_blob) return NULL; if (edid_blob->length == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "EDID blob was empty"); drmModeFreePropertyBlob (edid_blob); return NULL; } return g_bytes_new_with_free_func (edid_blob->data, edid_blob->length, (GDestroyNotify) drmModeFreePropertyBlob, edid_blob); } static gboolean output_get_tile_info (MetaMonitorManagerKms *manager_kms, MetaOutput *output) { MetaOutputKms *output_kms = output->driver_private; drmModePropertyBlobPtr tile_blob = NULL; int ret; if (output_kms->tile_blob_id == 0) return FALSE; tile_blob = drmModeGetPropertyBlob (manager_kms->fd, output_kms->tile_blob_id); if (!tile_blob) { meta_warning ("Failed to read TILE of output %s: %s\n", output->name, strerror(errno)); return FALSE; } if (tile_blob->length > 0) { ret = sscanf ((char *)tile_blob->data, "%d:%d:%d:%d:%d:%d:%d:%d", &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); drmModeFreePropertyBlob (tile_blob); if (ret != 8) { meta_warning ("Couldn't understand output tile property blob\n"); return FALSE; } return TRUE; } else { drmModeFreePropertyBlob (tile_blob); return FALSE; } } static MetaCrtcMode * find_meta_mode (MetaMonitorManager *manager, const drmModeModeInfo *drm_mode) { unsigned k; for (k = 0; k < manager->n_modes; k++) { if (drm_mode_equal (drm_mode, manager->modes[k].driver_private)) return &manager->modes[k]; } g_assert_not_reached (); return NULL; } static float drm_mode_vrefresh (const drmModeModeInfo *mode) { float refresh = 0.0; if (mode->htotal > 0 && mode->vtotal > 0) { /* Calculate refresh rate in milliHz first for extra precision. */ refresh = (mode->clock * 1000000LL) / mode->htotal; refresh += (mode->vtotal / 2); refresh /= mode->vtotal; if (mode->vscan > 1) refresh /= mode->vscan; refresh /= 1000.0; } return refresh; } static void init_mode (MetaCrtcMode *mode, const drmModeModeInfo *drm_mode, long mode_id) { 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 = drm_mode_vrefresh (drm_mode); mode->driver_private = g_slice_dup (drmModeModeInfo, drm_mode); mode->driver_notify = (GDestroyNotify)meta_monitor_mode_destroy_notify; } 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 MetaOutput * find_output_by_id (MetaOutput *outputs, unsigned n_outputs, glong id) { unsigned i; for (i = 0; i < n_outputs; i++) if (outputs[i].winsys_id == id) return &outputs[i]; return NULL; } static int find_property_index (MetaMonitorManager *manager, drmModeObjectPropertiesPtr props, const gchar *prop_name, drmModePropertyPtr *found) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); unsigned int i; for (i = 0; i < props->count_props; i++) { drmModePropertyPtr prop; prop = drmModeGetProperty (manager_kms->fd, props->props[i]); if (!prop) continue; if (strcmp (prop->name, prop_name) == 0) { *found = prop; return i; } drmModeFreeProperty (prop); } return -1; } static void parse_transforms (MetaMonitorManager *manager, drmModePropertyPtr prop, MetaCrtc *crtc) { MetaCrtcKms *crtc_kms = crtc->driver_private; int i; for (i = 0; i < prop->count_enums; i++) { int cur = -1; if (strcmp (prop->enums[i].name, "rotate-0") == 0) cur = META_MONITOR_TRANSFORM_NORMAL; else if (strcmp (prop->enums[i].name, "rotate-90") == 0) cur = META_MONITOR_TRANSFORM_90; else if (strcmp (prop->enums[i].name, "rotate-180") == 0) cur = META_MONITOR_TRANSFORM_180; else if (strcmp (prop->enums[i].name, "rotate-270") == 0) cur = META_MONITOR_TRANSFORM_270; if (cur != -1) { crtc_kms->all_hw_transforms |= 1 << cur; crtc_kms->rotation_map[cur] = 1 << prop->enums[i].value; } } } static gboolean is_primary_plane (MetaMonitorManager *manager, drmModeObjectPropertiesPtr props) { drmModePropertyPtr prop; int idx; idx = find_property_index (manager, props, "type", &prop); if (idx < 0) return FALSE; drmModeFreeProperty (prop); return props->prop_values[idx] == DRM_PLANE_TYPE_PRIMARY; } static void init_crtc_rotations (MetaMonitorManager *manager, MetaCrtc *crtc, unsigned int idx) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); drmModeObjectPropertiesPtr props; drmModePlaneRes *planes; drmModePlane *drm_plane; MetaCrtcKms *crtc_kms; unsigned int i; crtc_kms = crtc->driver_private; planes = drmModeGetPlaneResources(manager_kms->fd); if (planes == NULL) return; for (i = 0; i < planes->count_planes; i++) { drmModePropertyPtr prop; drm_plane = drmModeGetPlane (manager_kms->fd, planes->planes[i]); if (!drm_plane) continue; if ((drm_plane->possible_crtcs & (1 << idx))) { props = drmModeObjectGetProperties (manager_kms->fd, drm_plane->plane_id, DRM_MODE_OBJECT_PLANE); if (props && is_primary_plane (manager, props)) { int rotation_idx; crtc_kms->primary_plane_id = drm_plane->plane_id; rotation_idx = find_property_index (manager, props, "rotation", &prop); if (rotation_idx >= 0) { crtc_kms->rotation_prop_id = props->props[rotation_idx]; parse_transforms (manager, prop, crtc); drmModeFreeProperty (prop); } } if (props) drmModeFreeObjectProperties (props); } drmModeFreePlane (drm_plane); } crtc->all_transforms |= crtc_kms->all_hw_transforms; drmModeFreePlaneResources (planes); } static void add_common_modes (MetaMonitorManager *manager, MetaOutput *output) { const drmModeModeInfo *mode; GPtrArray *array; unsigned i; unsigned max_hdisplay = 0; unsigned max_vdisplay = 0; float max_vrefresh = 0.0; for (i = 0; i < output->n_modes; i++) { mode = output->modes[i]->driver_private; max_hdisplay = MAX (max_hdisplay, mode->hdisplay); max_vdisplay = MAX (max_vdisplay, mode->vdisplay); max_vrefresh = MAX (max_vrefresh, drm_mode_vrefresh (mode)); } max_vrefresh = MAX (max_vrefresh, 60.0); max_vrefresh *= (1 + SYNC_TOLERANCE); array = g_ptr_array_new (); for (i = 0; i < G_N_ELEMENTS (meta_default_drm_mode_infos); i++) { mode = &meta_default_drm_mode_infos[i]; if (mode->hdisplay > max_hdisplay || mode->vdisplay > max_vdisplay || drm_mode_vrefresh (mode) > max_vrefresh) continue; g_ptr_array_add (array, find_meta_mode (manager, 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 void init_crtc (MetaCrtc *crtc, MetaMonitorManager *manager, drmModeCrtc *drm_crtc) { unsigned int i; crtc->crtc_id = drm_crtc->crtc_id; crtc->rect.x = drm_crtc->x; crtc->rect.y = drm_crtc->y; crtc->rect.width = drm_crtc->width; crtc->rect.height = drm_crtc->height; crtc->is_dirty = FALSE; crtc->transform = META_MONITOR_TRANSFORM_NORMAL; crtc->all_transforms = meta_is_stage_views_enabled () ? ALL_TRANSFORMS_MASK : META_MONITOR_TRANSFORM_NORMAL; if (drm_crtc->mode_valid) { for (i = 0; i < manager->n_modes; i++) { if (drm_mode_equal (&drm_crtc->mode, manager->modes[i].driver_private)) { crtc->current_mode = &manager->modes[i]; break; } } } crtc->driver_private = g_new0 (MetaCrtcKms, 1); crtc->driver_notify = (GDestroyNotify) meta_crtc_destroy_notify; } static gboolean init_output (MetaOutput *output, MetaMonitorManager *manager, drmModeConnector *connector, MetaOutput *old_output) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); MetaOutputKms *output_kms; GArray *crtcs; GBytes *edid; unsigned int i; unsigned int crtc_mask; output_kms = g_slice_new0 (MetaOutputKms); output->driver_private = output_kms; output->driver_notify = (GDestroyNotify)meta_output_destroy_notify; output->winsys_id = connector->connector_id; output->name = make_output_name (connector); output->width_mm = connector->mmWidth; output->height_mm = connector->mmHeight; switch (connector->subpixel) { case DRM_MODE_SUBPIXEL_NONE: output->subpixel_order = COGL_SUBPIXEL_ORDER_NONE; break; case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB; break; case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR; break; case DRM_MODE_SUBPIXEL_VERTICAL_RGB: output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_RGB; break; case DRM_MODE_SUBPIXEL_VERTICAL_BGR: output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_BGR; break; case DRM_MODE_SUBPIXEL_UNKNOWN: default: output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN; break; } output->preferred_mode = NULL; output->n_modes = connector->count_modes; output->modes = g_new0 (MetaCrtcMode *, output->n_modes); for (i = 0; i < output->n_modes; i++) { output->modes[i] = find_meta_mode (manager, &connector->modes[i]); if (connector->modes[i].type & DRM_MODE_TYPE_PREFERRED) output->preferred_mode = output->modes[i]; } output_kms->connector = connector; find_connector_properties (manager_kms, output_kms); /* 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 (output_kms->has_scaling) add_common_modes (manager, output); if (!output->modes) { meta_monitor_manager_clear_output (output); return FALSE; } if (!output->preferred_mode) output->preferred_mode = output->modes[0]; qsort (output->modes, output->n_modes, sizeof (MetaCrtcMode *), compare_modes); output_kms->n_encoders = connector->count_encoders; output_kms->encoders = g_new0 (drmModeEncoderPtr, output_kms->n_encoders); crtc_mask = ~(unsigned int) 0; for (i = 0; i < output_kms->n_encoders; i++) { output_kms->encoders[i] = drmModeGetEncoder (manager_kms->fd, connector->encoders[i]); if (!output_kms->encoders[i]) continue; /* We only list CRTCs as supported if they are supported by all encoders for this connectors. This is what xf86-video-modesetting does (see drmmode_output_init()) */ crtc_mask &= output_kms->encoders[i]->possible_crtcs; if (output_kms->encoders[i]->encoder_id == connector->encoder_id) output_kms->current_encoder = output_kms->encoders[i]; } crtcs = g_array_new (FALSE, FALSE, sizeof (MetaCrtc*)); for (i = 0; i < manager->n_crtcs; i++) { if (crtc_mask & (1 << i)) { MetaCrtc *crtc = &manager->crtcs[i]; g_array_append_val (crtcs, crtc); } } output->n_possible_crtcs = crtcs->len; output->possible_crtcs = (void*)g_array_free (crtcs, FALSE); if (output_kms->current_encoder && output_kms->current_encoder->crtc_id != 0) { for (i = 0; i < manager->n_crtcs; i++) { if (manager->crtcs[i].crtc_id == output_kms->current_encoder->crtc_id) { output->crtc = &manager->crtcs[i]; break; } } } else { output->crtc = NULL; } 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 = output_kms->suggested_x; output->suggested_y = output_kms->suggested_y; output->hotplug_mode_update = output_kms->hotplug_mode_update; if (output_kms->edid_blob_id != 0) { GError *error = NULL; edid = read_output_edid (manager_kms, output, &error); if (!edid) { g_warning ("Failed to read EDID blob from %s: %s", output->name, error->message); g_error_free (error); } } else { edid = NULL; } meta_output_parse_edid (output, edid); g_bytes_unref (edid); /* MetaConnectorType matches DRM's connector types */ output->connector_type = (MetaConnectorType) connector->connector_type; output_get_tile_info (manager_kms, output); /* 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 TRUE; } static void detect_and_setup_output_clones (MetaMonitorManager *manager, drmModeRes *resources) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); drmModeEncoder **encoders; unsigned int i, n_encoders; n_encoders = (unsigned int) resources->count_encoders; encoders = g_new (drmModeEncoder *, n_encoders); for (i = 0; i < n_encoders; i++) encoders[i] = drmModeGetEncoder (manager_kms->fd, resources->encoders[i]); /* * Setup encoder position mask and encoder clone mask. */ for (i = 0; i < manager->n_outputs; i++) { MetaOutput *output; MetaOutputKms *output_kms; unsigned int j; output = &manager->outputs[i]; output_kms = output->driver_private; output_kms->enc_clone_mask = 0xff; output_kms->encoder_mask = 0; for (j = 0; j < output_kms->n_encoders; j++) { unsigned int k; for (k = 0; k < n_encoders; k++) { if (output_kms->encoders[j] && encoders[k] && output_kms->encoders[j]->encoder_id == encoders[k]->encoder_id) { output_kms->encoder_mask |= (1 << k); break; } } output_kms->enc_clone_mask &= output_kms->encoders[j]->possible_clones; } } for (i = 0; i < (unsigned)resources->count_encoders; i++) drmModeFreeEncoder (encoders[i]); g_free (encoders); /* * Setup MetaOutput <-> MetaOutput clone associations. */ for (i = 0; i < manager->n_outputs; i++) { MetaOutput *output; MetaOutputKms *output_kms; unsigned int j; output = &manager->outputs[i]; output_kms = output->driver_private; if (output_kms->enc_clone_mask == 0) continue; for (j = 0; j < manager->n_outputs; j++) { MetaOutput *meta_clone; MetaOutputKms *clone_kms; meta_clone = &manager->outputs[i]; clone_kms = meta_clone->driver_private; if (meta_clone == output) continue; if (clone_kms->encoder_mask == 0) continue; if (clone_kms->encoder_mask == output_kms->enc_clone_mask) { 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] = meta_clone; } } } } static void init_connectors (MetaMonitorManager *manager, drmModeRes *resources) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); unsigned int i; manager_kms->n_connectors = resources->count_connectors; manager_kms->connectors = g_new (drmModeConnector *, manager_kms->n_connectors); for (i = 0; i < manager_kms->n_connectors; i++) { drmModeConnector *drm_connector; drm_connector = drmModeGetConnector (manager_kms->fd, resources->connectors[i]); manager_kms->connectors[i] = drm_connector; } } static void init_modes (MetaMonitorManager *manager, drmModeRes *resources) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); GHashTable *modes; GHashTableIter iter; drmModeModeInfo *drm_mode; unsigned int i; long mode_id; /* * Gather all modes on all connected connectors. */ modes = g_hash_table_new (drm_mode_hash, drm_mode_equal); for (i = 0; i < manager_kms->n_connectors; i++) { drmModeConnector *drm_connector; drm_connector = manager_kms->connectors[i]; if (drm_connector && drm_connector->connection == DRM_MODE_CONNECTED) { unsigned int j; for (j = 0; j < (unsigned int) drm_connector->count_modes; j++) g_hash_table_add (modes, &drm_connector->modes[j]); } } manager->n_modes = g_hash_table_size (modes) + G_N_ELEMENTS (meta_default_drm_mode_infos); manager->modes = g_new0 (MetaCrtcMode, manager->n_modes); g_hash_table_iter_init (&iter, modes); mode_id = 0; while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &drm_mode)) { MetaCrtcMode *mode; mode = &manager->modes[mode_id]; init_mode (mode, drm_mode, (long) mode_id); mode_id++; } g_hash_table_destroy (modes); for (i = 0; i < G_N_ELEMENTS (meta_default_drm_mode_infos); i++) { MetaCrtcMode *mode; mode = &manager->modes[mode_id]; init_mode (mode, &meta_default_drm_mode_infos[i], (long) mode_id); mode_id++; } } static void init_crtcs (MetaMonitorManager *manager, drmModeRes *resources) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); unsigned int i; manager->n_crtcs = resources->count_crtcs; manager->crtcs = g_new0 (MetaCrtc, manager->n_crtcs); for (i = 0; i < (unsigned)resources->count_crtcs; i++) { drmModeCrtc *drm_crtc; MetaCrtc *crtc; drm_crtc = drmModeGetCrtc (manager_kms->fd, resources->crtcs[i]); crtc = &manager->crtcs[i]; init_crtc (crtc, manager, drm_crtc); find_crtc_properties (manager_kms, crtc); init_crtc_rotations (manager, crtc, i); drmModeFreeCrtc (drm_crtc); } } static void init_outputs (MetaMonitorManager *manager, drmModeRes *resources) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); MetaOutput *old_outputs; unsigned int n_old_outputs; unsigned int n_actual_outputs; unsigned int i; old_outputs = manager->outputs; n_old_outputs = manager->n_outputs; manager->outputs = g_new0 (MetaOutput, manager_kms->n_connectors); n_actual_outputs = 0; for (i = 0; i < manager_kms->n_connectors; i++) { drmModeConnector *connector; MetaOutput *output; connector = manager_kms->connectors[i]; output = &manager->outputs[n_actual_outputs]; if (connector && connector->connection == DRM_MODE_CONNECTED) { MetaOutput *old_output; old_output = find_output_by_id (old_outputs, n_old_outputs, connector->connector_id); if (init_output (output, manager, connector, old_output)) n_actual_outputs++; } } manager->n_outputs = n_actual_outputs; manager->outputs = g_renew (MetaOutput, manager->outputs, manager->n_outputs); /* Sort the outputs for easier handling in MetaMonitorConfig */ qsort (manager->outputs, manager->n_outputs, sizeof (MetaOutput), compare_outputs); detect_and_setup_output_clones (manager, resources); } static void meta_monitor_manager_kms_read_current (MetaMonitorManager *manager) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); drmModeRes *resources; resources = drmModeGetResources (manager_kms->fd); manager_kms->max_buffer_width = resources->max_width; manager_kms->max_buffer_height = resources->max_height; manager->power_save_mode = META_POWER_SAVE_ON; /* 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. */ free_resources (manager_kms); init_connectors (manager, resources); init_modes (manager, resources); init_crtcs (manager, resources); init_outputs (manager, resources); drmModeFreeResources (resources); } static GBytes * meta_monitor_manager_kms_read_edid (MetaMonitorManager *manager, MetaOutput *output) { MetaOutputKms *output_kms = output->driver_private; MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); GError *error = NULL; GBytes *edid; if (output_kms->edid_blob_id == 0) return NULL; edid = read_output_edid (manager_kms, output, &error); if (!edid) { g_warning ("Failed to read EDID from '%s': %s", output->name, error->message); g_error_free (error); return NULL; } return edid; } static void meta_monitor_manager_kms_set_power_save_mode (MetaMonitorManager *manager, MetaPowerSave mode) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); uint64_t state; unsigned i; 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; } for (i = 0; i < manager->n_outputs; i++) { MetaOutput *output; MetaOutputKms *output_kms; output = &manager->outputs[i]; output_kms = output->driver_private; if (output_kms->dpms_prop_id != 0) { int ok = drmModeObjectSetProperty (manager_kms->fd, output->winsys_id, DRM_MODE_OBJECT_CONNECTOR, output_kms->dpms_prop_id, state); if (ok < 0) meta_warning ("Failed to set power save mode for output %s: %s\n", output->name, strerror (errno)); } } } static void set_underscan (MetaMonitorManagerKms *manager_kms, MetaOutput *output) { if (!output->crtc) return; MetaCrtc *crtc = output->crtc; MetaCrtcKms *crtc_kms = crtc->driver_private; if (!crtc_kms->underscan_prop_id) return; if (output->is_underscanning) { drmModeObjectSetProperty (manager_kms->fd, crtc->crtc_id, DRM_MODE_OBJECT_CRTC, crtc_kms->underscan_prop_id, (uint64_t) 1); if (crtc_kms->underscan_hborder_prop_id) { uint64_t value = crtc->current_mode->width * 0.05; drmModeObjectSetProperty (manager_kms->fd, crtc->crtc_id, DRM_MODE_OBJECT_CRTC, crtc_kms->underscan_hborder_prop_id, value); } if (crtc_kms->underscan_vborder_prop_id) { uint64_t value = crtc->current_mode->height * 0.05; drmModeObjectSetProperty (manager_kms->fd, crtc->crtc_id, DRM_MODE_OBJECT_CRTC, crtc_kms->underscan_vborder_prop_id, value); } } else { drmModeObjectSetProperty (manager_kms->fd, crtc->crtc_id, DRM_MODE_OBJECT_CRTC, crtc_kms->underscan_prop_id, (uint64_t) 0); } } 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) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); unsigned i; for (i = 0; i < n_crtcs; i++) { MetaCrtcInfo *crtc_info = crtcs[i]; MetaCrtc *crtc = crtc_info->crtc; MetaCrtcKms *crtc_kms = crtc->driver_private; MetaMonitorTransform hw_transform; crtc->is_dirty = TRUE; if (crtc_info->mode == NULL) { crtc->rect.x = 0; crtc->rect.y = 0; crtc->rect.width = 0; crtc->rect.height = 0; crtc->current_mode = NULL; } else { MetaCrtcMode *mode; unsigned int j; int width, height; mode = crtc_info->mode; if (meta_monitor_transform_is_rotated (crtc_info->transform)) { width = mode->height; height = mode->width; } else { width = mode->width; height = mode->height; } crtc->rect.x = crtc_info->x; crtc->rect.y = crtc_info->y; crtc->rect.width = width; crtc->rect.height = height; crtc->current_mode = mode; crtc->transform = 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; output->crtc = crtc; } } if (crtc_kms->all_hw_transforms & (1 << crtc->transform)) hw_transform = crtc->transform; else hw_transform = META_MONITOR_TRANSFORM_NORMAL; if (drmModeObjectSetProperty (manager_kms->fd, crtc_kms->primary_plane_id, DRM_MODE_OBJECT_PLANE, crtc_kms->rotation_prop_id, crtc_kms->rotation_map[hw_transform]) != 0) { g_warning ("Failed to apply DRM plane transform %d: %m", hw_transform); /* Blacklist this HW transform, we want to fallback to our * fallbacks in this case. */ crtc_kms->all_hw_transforms &= ~(1 << hw_transform); } } /* Disable CRTCs not mentioned in the list (they have is_dirty == FALSE, because they weren't seen in the first loop) */ for (i = 0; i < manager->n_crtcs; i++) { MetaCrtc *crtc = &manager->crtcs[i]; crtc->logical_monitor = NULL; if (crtc->is_dirty) { crtc->is_dirty = FALSE; continue; } crtc->rect.x = 0; crtc->rect.y = 0; crtc->rect.width = 0; crtc->rect.height = 0; crtc->current_mode = NULL; } 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; set_underscan (manager_kms, output); } /* Disable outputs not mentioned in the list */ for (i = 0; i < manager->n_outputs; i++) { MetaOutput *output = &manager->outputs[i]; if (output->is_dirty) { output->is_dirty = FALSE; continue; } output->crtc = NULL; 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) { 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) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); drmModeCrtc *kms_crtc; kms_crtc = drmModeGetCrtc (manager_kms->fd, crtc->crtc_id); *size = kms_crtc->gamma_size; *red = g_new (unsigned short, *size); *green = g_new (unsigned short, *size); *blue = g_new (unsigned short, *size); drmModeCrtcGetGamma (manager_kms->fd, crtc->crtc_id, *size, *red, *green, *blue); drmModeFreeCrtc (kms_crtc); } static void meta_monitor_manager_kms_set_crtc_gamma (MetaMonitorManager *manager, MetaCrtc *crtc, gsize size, unsigned short *red, unsigned short *green, unsigned short *blue) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); drmModeCrtcSetGamma (manager_kms->fd, crtc->crtc_id, size, red, green, blue); } static void handle_hotplug_event (MetaMonitorManager *manager) { meta_monitor_manager_read_current_state (manager); meta_monitor_manager_on_hotplug (manager); } static void on_uevent (GUdevClient *client, const char *action, GUdevDevice *device, gpointer user_data) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (user_data); MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms); if (!g_udev_device_get_property_as_boolean (device, "HOTPLUG")) return; handle_hotplug_event (manager); } static gboolean kms_event_check (GSource *source) { MetaKmsSource *kms_source = (MetaKmsSource *) source; return g_source_query_unix_fd (source, kms_source->fd_tag) & G_IO_IN; } static gboolean kms_event_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { MetaKmsSource *kms_source = (MetaKmsSource *) source; meta_monitor_manager_kms_wait_for_flip (kms_source->manager_kms); return G_SOURCE_CONTINUE; } static GSourceFuncs kms_event_funcs = { NULL, kms_event_check, kms_event_dispatch }; static void meta_monitor_manager_kms_connect_uevent_handler (MetaMonitorManagerKms *manager_kms) { manager_kms->uevent_handler_id = g_signal_connect (manager_kms->udev, "uevent", G_CALLBACK (on_uevent), manager_kms); } static void meta_monitor_manager_kms_disconnect_uevent_handler (MetaMonitorManagerKms *manager_kms) { g_signal_handler_disconnect (manager_kms->udev, manager_kms->uevent_handler_id); manager_kms->uevent_handler_id = 0; } void meta_monitor_manager_kms_pause (MetaMonitorManagerKms *manager_kms) { meta_monitor_manager_kms_disconnect_uevent_handler (manager_kms); } void meta_monitor_manager_kms_resume (MetaMonitorManagerKms *manager_kms) { MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms); meta_monitor_manager_kms_connect_uevent_handler (manager_kms); handle_hotplug_event (manager); } static void meta_monitor_manager_kms_init (MetaMonitorManagerKms *manager_kms) { MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer); GSource *source; manager_kms->fd = meta_renderer_native_get_kms_fd (renderer_native); drmSetClientCap (manager_kms->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); const char *subsystems[2] = { "drm", NULL }; manager_kms->udev = g_udev_client_new (subsystems); meta_monitor_manager_kms_connect_uevent_handler (manager_kms); source = g_source_new (&kms_event_funcs, sizeof (MetaKmsSource)); manager_kms->source = (MetaKmsSource *) source; manager_kms->source->fd_tag = g_source_add_unix_fd (source, manager_kms->fd, G_IO_IN | G_IO_ERR); manager_kms->source->manager_kms = manager_kms; g_source_attach (source, NULL); } static void get_crtc_connectors (MetaMonitorManager *manager, MetaCrtc *crtc, uint32_t **connectors, unsigned int *n_connectors) { unsigned int i; GArray *connectors_array = g_array_new (FALSE, FALSE, sizeof (uint32_t)); for (i = 0; i < manager->n_outputs; i++) { MetaOutput *output = &manager->outputs[i]; if (output->crtc == crtc) g_array_append_val (connectors_array, output->winsys_id); } *n_connectors = connectors_array->len; *connectors = (uint32_t *) g_array_free (connectors_array, FALSE); } gboolean meta_monitor_manager_kms_apply_crtc_mode (MetaMonitorManagerKms *manager_kms, MetaCrtc *crtc, int x, int y, uint32_t fb_id) { MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms); uint32_t *connectors; unsigned int n_connectors; drmModeModeInfo *mode; get_crtc_connectors (manager, crtc, &connectors, &n_connectors); if (connectors) mode = crtc->current_mode->driver_private; else mode = NULL; if (drmModeSetCrtc (manager_kms->fd, crtc->crtc_id, fb_id, x, y, connectors, n_connectors, mode) != 0) { g_warning ("Failed to set CRTC mode %s: %m", crtc->current_mode->name); return FALSE; } g_free (connectors); return TRUE; } static void invoke_flip_closure (GClosure *flip_closure) { GValue param = G_VALUE_INIT; g_value_init (¶m, G_TYPE_POINTER); g_value_set_pointer (¶m, flip_closure); g_closure_invoke (flip_closure, NULL, 1, ¶m, NULL); g_closure_unref (flip_closure); } gboolean meta_monitor_manager_kms_is_crtc_active (MetaMonitorManagerKms *manager_kms, MetaCrtc *crtc) { MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms); unsigned int i; gboolean connected_crtc_found; if (manager->power_save_mode != META_POWER_SAVE_ON) return FALSE; connected_crtc_found = FALSE; for (i = 0; i < manager->n_outputs; i++) { MetaOutput *output = &manager->outputs[i]; if (output->crtc == crtc) { connected_crtc_found = TRUE; break; } } if (!connected_crtc_found) return FALSE; return TRUE; } gboolean meta_monitor_manager_kms_flip_crtc (MetaMonitorManagerKms *manager_kms, MetaCrtc *crtc, int x, int y, uint32_t fb_id, GClosure *flip_closure, gboolean *fb_in_use) { MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms); uint32_t *connectors; unsigned int n_connectors; int ret = -1; g_assert (manager->power_save_mode == META_POWER_SAVE_ON); get_crtc_connectors (manager, crtc, &connectors, &n_connectors); g_assert (n_connectors > 0); if (!manager_kms->page_flips_not_supported) { ret = drmModePageFlip (manager_kms->fd, crtc->crtc_id, fb_id, DRM_MODE_PAGE_FLIP_EVENT, flip_closure); if (ret != 0 && ret != -EACCES) { g_warning ("Failed to flip: %s", strerror (-ret)); manager_kms->page_flips_not_supported = TRUE; } } if (manager_kms->page_flips_not_supported) { if (meta_monitor_manager_kms_apply_crtc_mode (manager_kms, crtc, x, y, fb_id)) { *fb_in_use = TRUE; return FALSE; } } if (ret != 0) return FALSE; *fb_in_use = TRUE; g_closure_ref (flip_closure); return TRUE; } static void page_flip_handler (int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data) { GClosure *flip_closure = data; invoke_flip_closure (flip_closure); } void meta_monitor_manager_kms_wait_for_flip (MetaMonitorManagerKms *manager_kms) { drmEventContext evctx; if (manager_kms->page_flips_not_supported) return; memset (&evctx, 0, sizeof evctx); evctx.version = DRM_EVENT_CONTEXT_VERSION; evctx.page_flip_handler = page_flip_handler; drmHandleEvent (manager_kms->fd, &evctx); } static gboolean meta_monitor_manager_kms_is_transform_handled (MetaMonitorManager *manager, MetaCrtc *crtc, MetaMonitorTransform transform) { MetaCrtcKms *crtc_kms = crtc->driver_private; if ((1 << transform) & crtc_kms->all_hw_transforms) return TRUE; else return FALSE; } static float meta_monitor_manager_kms_calculate_monitor_mode_scale (MetaMonitorManager *manager, MetaMonitor *monitor, MetaMonitorMode *monitor_mode) { return meta_monitor_calculate_mode_scale (monitor, monitor_mode); } 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 = META_MONITOR_SCALES_CONSTRAINT_NONE; switch (layout_mode) { case META_LOGICAL_MONITOR_LAYOUT_MODE_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 MetaMonitorManagerCapability meta_monitor_manager_kms_get_capabilities (MetaMonitorManager *manager) { MetaBackend *backend = meta_get_backend (); MetaSettings *settings = meta_backend_get_settings (backend); MetaRenderer *renderer = meta_backend_get_renderer (backend); MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer); MetaMonitorManagerCapability capabilities = META_MONITOR_MANAGER_CAPABILITY_NONE; if (meta_settings_is_experimental_feature_enabled ( settings, META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER)) capabilities |= META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE; switch (meta_renderer_native_get_mode (renderer_native)) { case META_RENDERER_NATIVE_MODE_GBM: capabilities |= META_MONITOR_MANAGER_CAPABILITY_MIRRORING; break; #ifdef HAVE_EGL_DEVICE case META_RENDERER_NATIVE_MODE_EGL_DEVICE: break; #endif } return capabilities; } static gboolean meta_monitor_manager_kms_get_max_screen_size (MetaMonitorManager *manager, int *max_width, int *max_height) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager); if (meta_is_stage_views_enabled ()) return FALSE; *max_width = manager_kms->max_buffer_width; *max_height = manager_kms->max_buffer_height; return TRUE; } static MetaLogicalMonitorLayoutMode meta_monitor_manager_kms_get_default_layout_mode (MetaMonitorManager *manager) { MetaBackend *backend = meta_get_backend (); MetaSettings *settings = meta_backend_get_settings (backend); if (!meta_is_stage_views_enabled ()) return META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL; 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 void meta_monitor_manager_kms_dispose (GObject *object) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (object); g_clear_object (&manager_kms->udev); G_OBJECT_CLASS (meta_monitor_manager_kms_parent_class)->dispose (object); } static void meta_monitor_manager_kms_finalize (GObject *object) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (object); free_resources (manager_kms); g_source_destroy ((GSource *) manager_kms->source); G_OBJECT_CLASS (meta_monitor_manager_kms_parent_class)->finalize (object); } static void meta_monitor_manager_kms_class_init (MetaMonitorManagerKmsClass *klass) { MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = meta_monitor_manager_kms_dispose; object_class->finalize = meta_monitor_manager_kms_finalize; manager_class->read_current = meta_monitor_manager_kms_read_current; manager_class->read_edid = meta_monitor_manager_kms_read_edid; 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; } ukwm/src/backends/native/meta-monitor-manager-kms.h0000664000175000017500000000644713220600404021300 0ustar fengfeng/* -*- 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 "meta-monitor-manager-private.h" #define META_TYPE_MONITOR_MANAGER_KMS (meta_monitor_manager_kms_get_type ()) #define META_MONITOR_MANAGER_KMS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_MONITOR_MANAGER_KMS, MetaMonitorManagerKms)) #define META_MONITOR_MANAGER_KMS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_MONITOR_MANAGER_KMS, MetaMonitorManagerKmsClass)) #define META_IS_MONITOR_MANAGER_KMS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_MONITOR_MANAGER_KMS)) #define META_IS_MONITOR_MANAGER_KMS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_MONITOR_MANAGER_KMS)) #define META_MONITOR_MANAGER_KMS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_MONITOR_MANAGER_KMS, MetaMonitorManagerKmsClass)) typedef struct _MetaMonitorManagerKmsClass MetaMonitorManagerKmsClass; typedef struct _MetaMonitorManagerKms MetaMonitorManagerKms; GType meta_monitor_manager_kms_get_type (void); typedef void (*MetaKmsFlipCallback) (void *user_data); gboolean meta_monitor_manager_kms_apply_crtc_mode (MetaMonitorManagerKms *manager_kms, MetaCrtc *crtc, int x, int y, uint32_t fb_id); gboolean meta_monitor_manager_kms_is_crtc_active (MetaMonitorManagerKms *manager_kms, MetaCrtc *crtc); gboolean meta_monitor_manager_kms_flip_crtc (MetaMonitorManagerKms *manager_kms, MetaCrtc *crtc, int x, int y, uint32_t fb_id, GClosure *flip_closure, gboolean *fb_in_use); void meta_monitor_manager_kms_wait_for_flip (MetaMonitorManagerKms *manager_kms); void meta_monitor_manager_kms_pause (MetaMonitorManagerKms *manager_kms); void meta_monitor_manager_kms_resume (MetaMonitorManagerKms *manager_kms); #endif /* META_MONITOR_MANAGER_KMS_H */ ukwm/src/backends/native/meta-barrier-native.h0000664000175000017500000000536713233511035020330 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 ()) #define META_BARRIER_IMPL_NATIVE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BARRIER_IMPL_NATIVE, MetaBarrierImplNative)) #define META_BARRIER_IMPL_NATIVE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BARRIER_IMPL_NATIVE, MetaBarrierImplNativeClass)) #define META_IS_BARRIER_IMPL_NATIVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BARRIER_IMPL_NATIVE)) #define META_IS_BARRIER_IMPL_NATIVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BARRIER_IMPL_NATIVE)) #define META_BARRIER_IMPL_NATIVE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BARRIER_IMPL_NATIVE, MetaBarrierImplNativeClass)) typedef struct _MetaBarrierImplNative MetaBarrierImplNative; typedef struct _MetaBarrierImplNativeClass MetaBarrierImplNativeClass; typedef struct _MetaBarrierImplNativePrivate MetaBarrierImplNativePrivate; typedef struct _MetaBarrierManagerNative MetaBarrierManagerNative; struct _MetaBarrierImplNative { MetaBarrierImpl parent; }; struct _MetaBarrierImplNativeClass { MetaBarrierImplClass parent_class; }; GType meta_barrier_impl_native_get_type (void) G_GNUC_CONST; 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 */ ukwm/src/backends/native/meta-clutter-backend-native.c0000664000175000017500000000613313233511035021734 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #include "config.h" #include #include "backends/meta-backend-private.h" #include "backends/meta-renderer.h" #include "backends/native/meta-clutter-backend-native.h" #include "backends/native/meta-stage-native.h" #include "clutter/clutter.h" #include "meta/meta-backend.h" struct _MetaClutterBackendNative { ClutterBackendEglNative parent; MetaStageNative *stage_native; }; 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 (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; } ukwm/src/backends/native/meta-backend-native.c0000664000175000017500000005713013233511035020257 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "meta-backend-native.h" #include "meta-backend-native-private.h" #include #include #include #include "clutter/egl/clutter-egl.h" #include "clutter/evdev/clutter-evdev.h" #include "meta-barrier-native.h" #include "meta-border.h" #include "meta-idle-monitor-native.h" #include "meta-monitor-manager-kms.h" #include "meta-cursor-renderer-native.h" #include "meta-launcher.h" #include "backends/meta-cursor-tracker-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor-manager-private.h" #include "backends/meta-pointer-constraint.h" #include "backends/meta-stage.h" #include "backends/native/meta-clutter-backend-native.h" #include "backends/native/meta-input-settings-native.h" #include "backends/native/meta-renderer-native.h" #include "backends/native/meta-stage-native.h" #include struct _MetaBackendNative { MetaBackend parent; }; struct _MetaBackendNativePrivate { MetaLauncher *launcher; MetaBarrierManagerNative *barrier_manager; UpClient *up_client; guint sleep_signal_id; GCancellable *cancellable; GDBusConnection *system_bus; }; typedef struct _MetaBackendNativePrivate MetaBackendNativePrivate; G_DEFINE_TYPE_WITH_PRIVATE (MetaBackendNative, meta_backend_native, META_TYPE_BACKEND); static void meta_backend_native_finalize (GObject *object) { MetaBackendNative *native = META_BACKEND_NATIVE (object); MetaBackendNativePrivate *priv = meta_backend_native_get_instance_private (native); meta_launcher_free (priv->launcher); g_object_unref (priv->up_client); if (priv->sleep_signal_id) g_dbus_connection_signal_unsubscribe (priv->system_bus, priv->sleep_signal_id); g_cancellable_cancel (priv->cancellable); g_clear_object (&priv->cancellable); g_clear_object (&priv->system_bus); G_OBJECT_CLASS (meta_backend_native_parent_class)->finalize (object); } 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_native_reset_idletime (meta_idle_monitor_get_core ()); } static void system_bus_gotten_cb (GObject *object, GAsyncResult *res, gpointer user_data) { MetaBackendNativePrivate *priv; GDBusConnection *bus; bus = g_bus_get_finish (res, NULL); if (!bus) return; priv = meta_backend_native_get_instance_private (META_BACKEND_NATIVE (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); } static void lid_is_closed_changed_cb (UpClient *client, GParamSpec *pspec, gpointer user_data) { if (up_client_get_lid_is_closed (client)) return; meta_idle_monitor_native_reset_idletime (meta_idle_monitor_get_core ()); } static void constrain_to_barriers (ClutterInputDevice *device, guint32 time, float *new_x, float *new_y) { MetaBackendNative *native = META_BACKEND_NATIVE (meta_get_backend ()); MetaBackendNativePrivate *priv = meta_backend_native_get_instance_private (native); meta_barrier_manager_native_process (priv->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) { ClutterPoint 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 dx = *dx_inout, dy = *dy_inout; MetaScreenDirection 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 } }; if (direction != META_SCREEN_RIGHT && meta_line2_intersects_with (&motion, &left, &intersection)) direction = META_SCREEN_LEFT; else if (direction != META_SCREEN_LEFT && meta_line2_intersects_with (&motion, &right, &intersection)) direction = META_SCREEN_RIGHT; else if (direction != META_SCREEN_DOWN && meta_line2_intersects_with (&motion, &top, &intersection)) direction = META_SCREEN_UP; else if (direction != META_SCREEN_UP && meta_line2_intersects_with (&motion, &bottom, &intersection)) direction = META_SCREEN_DOWN; else { /* We reached the dest logical monitor */ x = motion.b.x; y = motion.b.y; 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 = x - cur_x; *dy_inout = 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) { ClutterDeviceManager *manager = clutter_device_manager_get_default (); META_BACKEND_CLASS (meta_backend_native_parent_class)->post_init (backend); clutter_evdev_set_pointer_constrain_callback (manager, pointer_constrain_callback, NULL, NULL); clutter_evdev_set_relative_motion_filter (manager, relative_motion_filter, meta_backend_get_monitor_manager (backend)); } static MetaIdleMonitor * meta_backend_native_create_idle_monitor (MetaBackend *backend, int device_id) { return g_object_new (META_TYPE_IDLE_MONITOR_NATIVE, "device-id", device_id, NULL); } static MetaMonitorManager * meta_backend_native_create_monitor_manager (MetaBackend *backend) { return g_object_new (META_TYPE_MONITOR_MANAGER_KMS, NULL); } static MetaCursorRenderer * meta_backend_native_create_cursor_renderer (MetaBackend *backend) { return g_object_new (META_TYPE_CURSOR_RENDERER_NATIVE, NULL); } static MetaRenderer * meta_backend_native_create_renderer (MetaBackend *backend) { MetaBackendNative *native = META_BACKEND_NATIVE (backend); MetaBackendNativePrivate *priv = meta_backend_native_get_instance_private (native); int kms_fd; const char *kms_file_path; GError *error = NULL; MetaRendererNative *renderer_native; kms_fd = meta_launcher_get_kms_fd (priv->launcher); kms_file_path = meta_launcher_get_kms_file_path (priv->launcher); renderer_native = meta_renderer_native_new (kms_fd, kms_file_path, &error); if (!renderer_native) { meta_warning ("Failed to create renderer: %s\n", error->message); g_error_free (error); 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 void meta_backend_native_warp_pointer (MetaBackend *backend, int x, int y) { ClutterDeviceManager *manager = clutter_device_manager_get_default (); ClutterInputDevice *device = clutter_device_manager_get_core_device (manager, CLUTTER_POINTER_DEVICE); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); /* XXX */ guint32 time_ = 0; /* Warp the input device pointer state. */ clutter_evdev_warp_pointer (device, time_, x, y); /* Warp displayed pointer cursor. */ meta_cursor_tracker_update_position (cursor_tracker, x, y); } 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) { ClutterDeviceManager *manager = clutter_device_manager_get_default (); 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 = 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); clutter_evdev_set_keyboard_map (manager, keymap); meta_backend_notify_keymap_changed (backend); xkb_keymap_unref (keymap); } static struct xkb_keymap * meta_backend_native_get_keymap (MetaBackend *backend) { ClutterDeviceManager *manager = clutter_device_manager_get_default (); return clutter_evdev_get_keyboard_map (manager); } static xkb_layout_index_t meta_backend_native_get_keymap_layout_group (MetaBackend *backend) { ClutterDeviceManager *manager = clutter_device_manager_get_default (); return clutter_evdev_get_keyboard_layout_index (manager); } static void meta_backend_native_lock_layout_group (MetaBackend *backend, guint idx) { ClutterDeviceManager *manager = clutter_device_manager_get_default (); xkb_layout_index_t old_idx; old_idx = meta_backend_native_get_keymap_layout_group (backend); if (old_idx == idx) return; clutter_evdev_set_keyboard_layout_index (manager, idx); meta_backend_notify_keymap_layout_group_changed (backend, idx); } static void meta_backend_native_set_numlock (MetaBackend *backend, gboolean numlock_state) { ClutterDeviceManager *manager = clutter_device_manager_get_default (); clutter_evdev_set_keyboard_numlock (manager, numlock_state); } static gboolean meta_backend_native_get_relative_motion_deltas (MetaBackend *backend, const ClutterEvent *event, double *dx, double *dy, double *dx_unaccel, double *dy_unaccel) { return clutter_evdev_event_get_relative_motion (event, dx, dy, dx_unaccel, dy_unaccel); } 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); if (meta_is_stage_views_enabled ()) meta_stage_native_rebuild_views (stage_native); else meta_stage_native_legacy_set_size (stage_native, width, height); clutter_actor_set_size (stage, width, height); } 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_idle_monitor = meta_backend_native_create_idle_monitor; 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->warp_pointer = meta_backend_native_warp_pointer; 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->get_relative_motion_deltas = meta_backend_native_get_relative_motion_deltas; 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) { MetaBackendNativePrivate *priv = meta_backend_native_get_instance_private (native); GError *error = NULL; priv->launcher = meta_launcher_new (&error); if (priv->launcher == NULL) { g_warning ("Can't initialize KMS backend: %s\n", error->message); exit (1); } priv->barrier_manager = meta_barrier_manager_native_new (); priv->up_client = up_client_new (); g_signal_connect (priv->up_client, "notify::lid-is-closed", G_CALLBACK (lid_is_closed_changed_cb), NULL); priv->cancellable = g_cancellable_new (); g_bus_get (G_BUS_TYPE_SYSTEM, priv->cancellable, system_bus_gotten_cb, native); } gboolean meta_activate_vt (int vt, GError **error) { MetaBackend *backend = meta_get_backend (); MetaBackendNative *native = META_BACKEND_NATIVE (backend); MetaBackendNativePrivate *priv = meta_backend_native_get_instance_private (native); return meta_launcher_activate_vt (priv->launcher, vt, error); } MetaBarrierManagerNative * meta_backend_native_get_barrier_manager (MetaBackendNative *native) { MetaBackendNativePrivate *priv = meta_backend_native_get_instance_private (native); return priv->barrier_manager; } /** * meta_activate_session: * * Tells ukwm to activate the session. When ukwm 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); MetaBackendNativePrivate *priv = meta_backend_native_get_instance_private (native); if (!meta_launcher_activate_session (priv->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); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerKms *monitor_manager_kms = META_MONITOR_MANAGER_KMS (monitor_manager); clutter_evdev_release_devices (); clutter_egl_freeze_master_clock (); meta_monitor_manager_kms_pause (monitor_manager_kms); } void meta_backend_native_resume (MetaBackendNative *native) { MetaBackend *backend = META_BACKEND (native); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerKms *monitor_manager_kms = META_MONITOR_MANAGER_KMS (monitor_manager); MetaCursorRenderer *cursor_renderer; MetaCursorRendererNative *cursor_renderer_native; ClutterActor *stage; MetaIdleMonitor *idle_monitor; meta_monitor_manager_kms_resume (monitor_manager_kms); clutter_evdev_reclaim_devices (); clutter_egl_thaw_master_clock (); stage = meta_backend_get_stage (backend); clutter_actor_queue_redraw (stage); cursor_renderer = meta_backend_get_cursor_renderer (backend); cursor_renderer_native = META_CURSOR_RENDERER_NATIVE (cursor_renderer); meta_cursor_renderer_native_force_update (cursor_renderer_native); idle_monitor = meta_backend_get_idle_monitor (backend, 0); meta_idle_monitor_native_reset_idletime (idle_monitor); } ukwm/src/backends/meta-cursor-tracker.c0000664000175000017500000002700213220600404017052 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2013 Red Hat, Inc. * Copyright 2017 Tianjin KYLIN Information Technology Co., 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, see . * * Author: Giovanni Campagna */ /** * SECTION:cursor-tracker * @title: MetaCursorTracker * @short_description: Ukwm cursor tracking helper. Originally only * tracking the cursor image, now more of a "core * pointer abstraction" */ #include "config.h" #include "meta-cursor-tracker-private.h" #include #include #include #include #include #include #include #include #include #include "meta-backend-private.h" G_DEFINE_TYPE (MetaCursorTracker, meta_cursor_tracker, G_TYPE_OBJECT); enum { CURSOR_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; static MetaCursorSprite * get_displayed_cursor (MetaCursorTracker *tracker) { MetaDisplay *display = meta_get_display (); if (!tracker->is_showing) return NULL; if (meta_display_windows_are_interactable (display)) { if (tracker->has_window_cursor) return tracker->window_cursor; } return tracker->root_cursor; } static void update_displayed_cursor (MetaCursorTracker *tracker) { MetaBackend *backend = meta_get_backend (); MetaCursorRenderer *cursor_renderer = meta_backend_get_cursor_renderer (backend); meta_cursor_renderer_set_cursor (cursor_renderer, tracker->displayed_cursor); } static void sync_cursor (MetaCursorTracker *tracker) { MetaCursorSprite *displayed_cursor = get_displayed_cursor (tracker); if (tracker->displayed_cursor == displayed_cursor) return; g_clear_object (&tracker->displayed_cursor); if (displayed_cursor) tracker->displayed_cursor = g_object_ref (displayed_cursor); update_displayed_cursor (tracker); 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->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); } /** * meta_cursor_tracker_get_for_screen: * @screen: the #MetaScreen * * Retrieves the cursor tracker object for @screen. * * Returns: (transfer none): */ MetaCursorTracker * meta_cursor_tracker_get_for_screen (MetaScreen *screen) { 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) { MetaDisplay *display = meta_get_display (); XFixesCursorNotifyEvent *notify_event; if (meta_is_wayland_compositor ()) return FALSE; if (xevent->xany.type != 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 (); XFixesCursorImage *cursor_image; CoglTexture2D *sprite; guint8 *cursor_data; gboolean free_cursor_data; CoglContext *ctx; CoglError *error = NULL; if (tracker->xfixes_cursor) return; cursor_image = XFixesGetCursorImage (display->xdisplay); if (!cursor_image) return; /* 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 = (guint8 *)cursor_image->pixels; free_cursor_data = FALSE; } else { int i, j; guint32 *cursor_words; gulong *p; guint32 *q; cursor_words = g_new (guint32, cursor_image->width * cursor_image->height); cursor_data = (guint8 *)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; } ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); sprite = cogl_texture_2d_new_from_data (ctx, 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 (error != NULL) { meta_warning ("Failed to allocate cursor sprite texture: %s\n", error->message); cogl_error_free (error); } if (sprite != NULL) { MetaCursorSprite *cursor_sprite = meta_cursor_sprite_new (); meta_cursor_sprite_set_texture (cursor_sprite, COGL_TEXTURE (sprite), cursor_image->xhot, cursor_image->yhot); cogl_object_unref (sprite); tracker->xfixes_cursor = cursor_sprite; } XFree (cursor_image); } /** * 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 = tracker->xfixes_cursor; } if (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 = 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); } 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); } 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) { ClutterDeviceManager *cmanager; ClutterInputDevice *cdevice; ClutterPoint point; cmanager = clutter_device_manager_get_default (); cdevice = clutter_device_manager_get_core_device (cmanager, CLUTTER_POINTER_DEVICE); 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); } void meta_cursor_tracker_set_pointer_visible (MetaCursorTracker *tracker, gboolean visible) { if (visible == tracker->is_showing) return; tracker->is_showing = visible; sync_cursor (tracker); } MetaCursorSprite * meta_cursor_tracker_get_displayed_cursor (MetaCursorTracker *tracker) { return tracker->displayed_cursor; } ukwm/src/backends/meta-cursor.c0000664000175000017500000002265013220600404015425 0ustar fengfeng/* -*- 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 "meta-cursor.h" #include #include "display-private.h" #include "screen-private.h" #include "meta-backend-private.h" #include #include #include #include enum { PREPARE_AT, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; struct _MetaCursorSprite { GObject parent; MetaCursor cursor; CoglTexture2D *texture; float texture_scale; int hot_x, hot_y; int current_frame; XcursorImages *xcursor_images; int theme_scale; gboolean theme_dirty; }; G_DEFINE_TYPE (MetaCursorSprite, meta_cursor_sprite, G_TYPE_OBJECT) 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"; default: break; } g_assert_not_reached (); } Cursor meta_cursor_create_x_cursor (Display *xdisplay, MetaCursor cursor) { return XcursorLibraryLoadCursor (xdisplay, translate_meta_cursor (cursor)); } static XcursorImages * load_cursor_on_client (MetaCursor cursor, int scale) { return XcursorLibraryLoadImages (translate_meta_cursor (cursor), meta_prefs_get_cursor_theme (), meta_prefs_get_cursor_size () * scale); } static void meta_cursor_sprite_load_from_xcursor_image (MetaCursorSprite *self, XcursorImage *xc_image) { MetaBackend *meta_backend = meta_get_backend (); MetaCursorRenderer *renderer = meta_backend_get_cursor_renderer (meta_backend); uint width, height, rowstride; CoglPixelFormat cogl_format; ClutterBackend *clutter_backend; CoglContext *cogl_context; CoglTexture2D *texture; CoglError *error = NULL; g_assert (self->texture == NULL); width = xc_image->width; height = 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 (error) { meta_warning ("Failed to allocate cursor texture: %s\n", error->message); cogl_error_free (error); } meta_cursor_sprite_set_texture (self, COGL_TEXTURE (texture), xc_image->xhot, xc_image->yhot); if (texture) cogl_object_unref (texture); meta_cursor_renderer_realize_cursor_from_xcursor (renderer, self, xc_image); } static XcursorImage * meta_cursor_sprite_get_current_frame_image (MetaCursorSprite *self) { return self->xcursor_images->images[self->current_frame]; } void meta_cursor_sprite_tick_frame (MetaCursorSprite *self) { XcursorImage *image; if (!meta_cursor_sprite_is_animated (self)) return; self->current_frame++; if (self->current_frame >= self->xcursor_images->nimage) self->current_frame = 0; image = meta_cursor_sprite_get_current_frame_image (self); g_clear_pointer (&self->texture, cogl_object_unref); meta_cursor_sprite_load_from_xcursor_image (self, image); } guint meta_cursor_sprite_get_current_frame_time (MetaCursorSprite *self) { if (!meta_cursor_sprite_is_animated (self)) return 0; return self->xcursor_images->images[self->current_frame]->delay; } gboolean meta_cursor_sprite_is_animated (MetaCursorSprite *self) { return (self->xcursor_images && self->xcursor_images->nimage > 1); } MetaCursorSprite * meta_cursor_sprite_new (void) { return g_object_new (META_TYPE_CURSOR_SPRITE, NULL); } static void meta_cursor_sprite_load_from_theme (MetaCursorSprite *self) { XcursorImage *image; g_assert (self->cursor != META_CURSOR_NONE); /* We might be reloading with a different scale. If so clear the old data. */ if (self->xcursor_images) { g_clear_pointer (&self->texture, cogl_object_unref); XcursorImagesDestroy (self->xcursor_images); } self->current_frame = 0; self->xcursor_images = load_cursor_on_client (self->cursor, self->theme_scale); if (!self->xcursor_images) meta_fatal ("Could not find cursor. Perhaps set XCURSOR_PATH?"); image = meta_cursor_sprite_get_current_frame_image (self); meta_cursor_sprite_load_from_xcursor_image (self, image); self->theme_dirty = FALSE; } MetaCursorSprite * meta_cursor_sprite_from_theme (MetaCursor cursor) { MetaCursorSprite *self; self = meta_cursor_sprite_new (); self->cursor = cursor; self->theme_dirty = TRUE; return self; } void meta_cursor_sprite_set_texture (MetaCursorSprite *self, CoglTexture *texture, int hot_x, int hot_y) { g_clear_pointer (&self->texture, cogl_object_unref); if (texture) self->texture = cogl_object_ref (texture); self->hot_x = hot_x; self->hot_y = hot_y; } void meta_cursor_sprite_set_texture_scale (MetaCursorSprite *self, float scale) { self->texture_scale = scale; } void meta_cursor_sprite_set_theme_scale (MetaCursorSprite *self, int theme_scale) { if (self->theme_scale != theme_scale) self->theme_dirty = TRUE; self->theme_scale = theme_scale; } CoglTexture * meta_cursor_sprite_get_cogl_texture (MetaCursorSprite *self) { return COGL_TEXTURE (self->texture); } MetaCursor meta_cursor_sprite_get_meta_cursor (MetaCursorSprite *self) { return self->cursor; } void meta_cursor_sprite_get_hotspot (MetaCursorSprite *self, int *hot_x, int *hot_y) { *hot_x = self->hot_x; *hot_y = self->hot_y; } float meta_cursor_sprite_get_texture_scale (MetaCursorSprite *self) { return self->texture_scale; } void meta_cursor_sprite_prepare_at (MetaCursorSprite *self, int x, int y) { g_signal_emit (self, signals[PREPARE_AT], 0, x, y); } void meta_cursor_sprite_realize_texture (MetaCursorSprite *self) { if (self->theme_dirty) meta_cursor_sprite_load_from_theme (self); } static void meta_cursor_sprite_init (MetaCursorSprite *self) { self->texture_scale = 1.0f; } static void meta_cursor_sprite_finalize (GObject *object) { MetaCursorSprite *self = META_CURSOR_SPRITE (object); if (self->xcursor_images) XcursorImagesDestroy (self->xcursor_images); g_clear_pointer (&self->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); } ukwm/src/backends/x11/0000775000175000017500000000000013254604521013437 5ustar fengfengukwm/src/backends/x11/meta-backend-x11.h0000664000175000017500000000400413233511035016522 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_BACKEND_X11_H #define META_BACKEND_X11_H #include "backends/meta-backend-private.h" #include #include #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); #endif /* META_BACKEND_X11_H */ ukwm/src/backends/x11/meta-cursor-renderer-x11.c0000664000175000017500000000641113233511035020253 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "meta-cursor-renderer-x11.h" #include #include "meta-backend-x11.h" #include "meta-stage.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) { MetaCursor cursor = meta_cursor_sprite_get_meta_cursor (cursor_sprite); if (cursor != META_CURSOR_NONE) { Cursor xcursor = meta_cursor_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; } ukwm/src/backends/x11/meta-barrier-x11.h0000664000175000017500000000424713233511035016572 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 ()) #define META_BARRIER_IMPL_X11(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BARRIER_IMPL_X11, MetaBarrierImplX11)) #define META_BARRIER_IMPL_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BARRIER_IMPL_X11, MetaBarrierImplX11Class)) #define META_IS_BARRIER_IMPL_X11(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BARRIER_IMPL_X11)) #define META_IS_BARRIER_IMPL_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BARRIER_IMPL_X11)) #define META_BARRIER_IMPL_X11_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BARRIER_IMPL_X11, MetaBarrierImplX11Class)) typedef struct _MetaBarrierImplX11 MetaBarrierImplX11; typedef struct _MetaBarrierImplX11Class MetaBarrierImplX11Class; typedef struct _MetaBarrierImplX11Private MetaBarrierImplX11Private; struct _MetaBarrierImplX11 { MetaBarrierImpl parent; }; struct _MetaBarrierImplX11Class { MetaBarrierImplClass parent_class; }; GType meta_barrier_impl_x11_get_type (void) G_GNUC_CONST; MetaBarrierImpl *meta_barrier_impl_x11_new (MetaBarrier *barrier); G_END_DECLS #endif /* META_BARRIER_X11_H1 */ ukwm/src/backends/x11/meta-clutter-backend-x11.c0000664000175000017500000000553213233511035020204 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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-stage-x11-nested.h" #include "clutter/clutter.h" #include "clutter/clutter-ukwm.h" #include "meta/meta-backend.h" struct _MetaClutterBackendX11 { ClutterBackendX11 parent; }; 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) { ClutterEventTranslator *translator; ClutterStageWindow *stage; GType stage_type; if (meta_is_wayland_compositor ()) stage_type = META_TYPE_STAGE_X11_NESTED; else stage_type = CLUTTER_TYPE_STAGE_X11; stage = g_object_new (stage_type, "backend", backend, "wrapper", wrapper, NULL); /* the X11 stage does event translation */ translator = CLUTTER_EVENT_TRANSLATOR (stage); _clutter_backend_add_event_translator (backend, translator); return stage; } 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; } ukwm/src/backends/x11/meta-clutter-backend-x11.h0000664000175000017500000000247213233511035020211 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 */ ukwm/src/backends/x11/cm/0000775000175000017500000000000013254604521014036 5ustar fengfengukwm/src/backends/x11/cm/meta-backend-x11-cm.h0000664000175000017500000000213513233511035017521 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 */ ukwm/src/backends/x11/cm/meta-renderer-x11-cm.h0000664000175000017500000000225213233511035017740 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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) #endif /* META_RENDERER_X11_CM_H */ ukwm/src/backends/x11/cm/meta-renderer-x11-cm.c0000664000175000017500000000230313233511035017730 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * */ #include "config.h" #include "backends/x11/cm/meta-renderer-x11-cm.h" struct _MetaRendererX11Cm { MetaRendererX11 parent; }; G_DEFINE_TYPE (MetaRendererX11Cm, meta_renderer_x11_cm, META_TYPE_RENDERER_X11) static void meta_renderer_x11_cm_init (MetaRendererX11Cm *renderer_x11_cm) { } static void meta_renderer_x11_cm_class_init (MetaRendererX11CmClass *klass) { } ukwm/src/backends/x11/cm/meta-backend-x11-cm.c0000664000175000017500000003131313233511035017514 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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/x11/meta-cursor-renderer-x11.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" 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 (ClutterDeviceManager *device_manager, 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); parent_backend_class->post_init (backend); g_signal_connect_object (clutter_device_manager_get_default (), "device-added", G_CALLBACK (on_device_added), backend, 0); take_touch_grab (backend); } static MetaRenderer * meta_backend_x11_cm_create_renderer (MetaBackend *backend) { return g_object_new (META_TYPE_RENDERER_X11_CM, NULL); } static MetaMonitorManager * meta_backend_x11_cm_create_monitor_manager (MetaBackend *backend) { return g_object_new (META_TYPE_MONITOR_MANAGER_XRANDR, 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); 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) { if (x11_cm->locked_group != xkb_ev->state.locked_group) XkbLockGroup (xdisplay, XkbUseCoreKbd, x11_cm->locked_group); } break; default: break; } } } return meta_monitor_manager_xrandr_handle_xevent (monitor_manager_xrandr, event); } 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) { } 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; } ukwm/src/backends/x11/meta-renderer-x11.c0000664000175000017500000000625513233511035016746 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #include "config.h" #include #include "clutter/x11/clutter-x11.h" #include "cogl/cogl.h" #include "cogl/cogl-xlib.h" #include "cogl/winsys/cogl-winsys-glx-private.h" #include "cogl/winsys/cogl-winsys-egl-x11-private.h" #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-renderer.h" #include "backends/meta-renderer-view.h" #include "backends/x11/meta-renderer-x11.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) { if (meta_is_wayland_compositor ()) return _cogl_winsys_egl_xlib_get_vtable (); switch (renderer->driver) { case COGL_DRIVER_GLES1: case COGL_DRIVER_GLES2: return _cogl_winsys_egl_xlib_get_vtable (); case COGL_DRIVER_GL: case COGL_DRIVER_GL3: return _cogl_winsys_glx_get_vtable (); case COGL_DRIVER_ANY: case COGL_DRIVER_NOP: case COGL_DRIVER_WEBGL: break; } g_assert_not_reached (); } 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); cogl_xlib_renderer_set_foreign_display (cogl_renderer, xdisplay); /* Set up things so that if the INTEL_swap_event extension is not present, * but the driver is known to have good thread support, we use an extra * thread and call glXWaitVideoSync() in the thread. This allows idles * to work properly, even when Ukwm is constantly redrawing new frames; * otherwise, without INTEL_swap_event, we'll just block in glXSwapBuffers(). */ cogl_xlib_renderer_set_threaded_swap_wait_enabled (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; } ukwm/src/backends/x11/meta-input-settings-x11.c0000664000175000017500000006550613233511035020141 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Author: Carlos Garnacho */ #include "config.h" #include "meta-backend-x11.h" #include "meta-input-settings-x11.h" #include #include #include #include #include #ifdef HAVE_LIBGUDEV #include #endif #include #include "backends/meta-logical-monitor.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 * 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); rc = XIGetProperty (xdisplay, device_id, property_atom, 0, 10, False, type, &type_ret, &format_ret, &nitems_ret, &bytes_after_ret, &data_ret); if (rc == Success && type_ret == type && format_ret == format && nitems_ret >= nitems) { if (nitems_ret > nitems) g_warning ("Property '%s' for device '%s' returned %lu items, expected %lu", property, clutter_input_device_get_device_name (device), 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, GDesktopDeviceSendEvents 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); if (!available) return; switch (mode) { case G_DESKTOP_DEVICE_SEND_EVENTS_DISABLED: values[0] = 1; break; case G_DESKTOP_DEVICE_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE: values[1] = 1; break; default: break; } 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 value = speed; change_property (device, "libinput Accel Speed", XInternAtom (xdisplay, "FLOAT", False), 32, &value, 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); } static void meta_input_settings_x11_set_tap_enabled (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled) { guchar value = (enabled) ? 1 : 0; change_property (device, "libinput Tapping Enabled", XA_INTEGER, 8, &value, 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 TappingDrag Enabled", XA_INTEGER, 8, &value, 1); } static void meta_input_settings_x11_set_invert_scroll (MetaInputSettings *settings, ClutterInputDevice *device, gboolean inverted) { 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; 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; 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, "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 (MetaInputSettings *settings, ClutterInputDevice *device, GDesktopTouchpadClickMethod mode) { guchar values[2] = { 0 }; /* buttonareas, clickfinger */ guchar *defaults, *available; available = get_property (device, "libinput Click Methods Available", XA_INTEGER, 8, 2); if (!available) return; switch (mode) { case G_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 G_DESKTOP_TOUCHPAD_CLICK_METHOD_NONE: break; case G_DESKTOP_TOUCHPAD_CLICK_METHOD_AREAS: values[0] = 1; break; case G_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 g_warning ("Failed to set acceleration profile: no udev support"); 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 is_trackball (MetaInputSettings *settings, ClutterInputDevice *device) { return meta_input_device_is_trackball (device); } static void set_device_accel_profile (ClutterInputDevice *device, GDesktopPointerAccelProfile 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 G_DESKTOP_POINTER_ACCEL_PROFILE_FLAT: values[0] = 0; values[1] = 1; break; case G_DESKTOP_POINTER_ACCEL_PROFILE_ADAPTIVE: values[0] = 1; values[1] = 0; break; default: g_warn_if_reached (); case G_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, GDesktopPointerAccelProfile 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, GDesktopPointerAccelProfile profile) { if (!is_trackball (settings, device)) return; set_device_accel_profile (device, profile); } static void meta_input_settings_x11_set_tablet_mapping (MetaInputSettings *settings, ClutterInputDevice *device, GDesktopTabletMapping mapping) { 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; if (!display) return; /* Grab the puke bucket! */ meta_error_trap_push (display); xdev = XOpenDevice (xdisplay, device_id); if (xdev) { XSetDeviceMode (xdisplay, xdev, mapping == G_DESKTOP_TABLET_MAPPING_ABSOLUTE ? Absolute : Relative); XCloseDevice (xdisplay, xdev); } if (meta_error_trap_pop_with_return (display)) { g_warning ("Could not set tablet mapping for %s", clutter_input_device_get_device_name (device)); } else { ClutterInputDeviceMapping dev_mapping; dev_mapping = (mapping == G_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 (GDesktopStylusButtonAction action, guint button) { switch (action) { case G_DESKTOP_STYLUS_BUTTON_ACTION_MIDDLE: return CLUTTER_BUTTON_MIDDLE; case G_DESKTOP_STYLUS_BUTTON_ACTION_RIGHT: return CLUTTER_BUTTON_SECONDARY; case G_DESKTOP_STYLUS_BUTTON_ACTION_BACK: return 8; case G_DESKTOP_STYLUS_BUTTON_ACTION_FORWARD: return 9; case G_DESKTOP_STYLUS_BUTTON_ACTION_DEFAULT: default: return button; } } static void meta_input_settings_x11_set_stylus_button_map (MetaInputSettings *settings, ClutterInputDevice *device, ClutterInputDeviceTool *tool, GDesktopStylusButtonAction primary, GDesktopStylusButtonAction secondary) { 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; if (!display) return; /* Grab the puke bucket! */ meta_error_trap_push (display); xdev = XOpenDevice (xdisplay, device_id); if (xdev) { guchar map[3] = { CLUTTER_BUTTON_PRIMARY, action_to_button (primary, CLUTTER_BUTTON_MIDDLE), action_to_button (secondary, CLUTTER_BUTTON_SECONDARY), }; XSetDeviceButtonMapping (xdisplay, xdev, map, G_N_ELEMENTS (map)); XCloseDevice (xdisplay, xdev); } if (meta_error_trap_pop_with_return (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_stylus_pressure (MetaInputSettings *settings, ClutterInputDevice *device, ClutterInputDeviceTool *tool, const gint32 pressure[4]) { guchar values[4] = { pressure[0], pressure[1], pressure[2], pressure[3] }; change_property (device, "Wacom Pressurecurve", XA_INTEGER, 8, &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->has_two_finger_scroll = meta_input_settings_x11_has_two_finger_scroll; } 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 } ukwm/src/backends/x11/meta-stage-x11-nested.h0000664000175000017500000000242213233511035017520 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #ifndef META_STAGE_X11_NESTED_H #define META_STAGE_X11_NESTED_H #include "clutter/clutter-ukwm.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, ClutterStageX11) #endif /* META_STAGE_X11_NESTED_H */ ukwm/src/backends/x11/meta-monitor-manager-xrandr.c0000664000175000017500000020176613220600404021123 0ustar fengfeng/* -*- 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. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #include "config.h" #include "meta-monitor-manager-xrandr.h" #include #include #include #include #include #include #include #include #include #include #include #include "meta-backend-x11.h" #include #include #include "backends/meta-monitor-config-manager.h" #include "backends/meta-logical-monitor.h" #define ALL_TRANSFORMS ((1 << (META_MONITOR_TRANSFORM_FLIPPED_270 + 1)) - 1) /* 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 struct _MetaMonitorManagerXrandr { MetaMonitorManager parent_instance; Display *xdisplay; XRRScreenResources *resources; int rr_event_base; int rr_error_base; gboolean has_randr15; xcb_timestamp_t last_xrandr_set_timestamp; #ifdef HAVE_XRANDR15 GHashTable *tiled_monitor_atoms; #endif /* HAVE_XRANDR15 */ int max_screen_width; int max_screen_height; float *supported_scales; int n_supported_scales; }; struct _MetaMonitorManagerXrandrClass { MetaMonitorManagerClass parent_class; }; G_DEFINE_TYPE (MetaMonitorManagerXrandr, meta_monitor_manager_xrandr, META_TYPE_MONITOR_MANAGER); #ifdef HAVE_XRANDR15 typedef struct _MetaMonitorXrandrData { Atom xrandr_name; } MetaMonitorXrandrData; GQuark quark_meta_monitor_xrandr_data; #endif /* HAVE_RANDR15 */ 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; } static gboolean output_get_integer_property (MetaMonitorManagerXrandr *manager_xrandr, MetaOutput *output, const char *propname, gint *value) { gboolean exists = FALSE; Atom atom, actual_type; int actual_format; unsigned long nitems, bytes_after; unsigned char *buffer; atom = XInternAtom (manager_xrandr->xdisplay, propname, False); XRRGetOutputProperty (manager_xrandr->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 (MetaMonitorManagerXrandr *manager_xrandr, MetaOutput *output, const char *propname) { gboolean exists = FALSE; Atom atom, actual_type; int actual_format; unsigned long nitems, bytes_after; unsigned char *buffer; atom = XInternAtom (manager_xrandr->xdisplay, propname, False); XRRGetOutputProperty (manager_xrandr->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 (MetaMonitorManagerXrandr *manager_xrandr, MetaOutput *output, const char *propname) { Atom atom, actual_type; int actual_format; unsigned long nitems, bytes_after; g_autofree unsigned char *buffer = NULL; atom = XInternAtom (manager_xrandr->xdisplay, propname, False); XRRGetOutputProperty (manager_xrandr->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 (MetaMonitorManagerXrandr *manager_xrandr, MetaOutput *output) { return output_get_boolean_property (manager_xrandr, output, "_UKWM_PRESENTATION_OUTPUT"); } static gboolean output_get_underscanning_xrandr (MetaMonitorManagerXrandr *manager_xrandr, MetaOutput *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 (manager_xrandr->xdisplay, "underscan", False); XRRGetOutputProperty (manager_xrandr->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 (manager_xrandr->xdisplay, *(Atom *)buffer); return (strcmp (str, "on") == 0); } static gboolean output_get_supports_underscanning_xrandr (MetaMonitorManagerXrandr *manager_xrandr, MetaOutput *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 (manager_xrandr->xdisplay, "underscan", False); XRRGetOutputProperty (manager_xrandr->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 (manager_xrandr->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 (manager_xrandr->xdisplay, values[i]); if (strcmp (name, "on") == 0) supports_underscanning = TRUE; XFree (name); } XFree (property_info); return supports_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); } static int output_get_backlight_xrandr (MetaMonitorManagerXrandr *manager_xrandr, MetaOutput *output) { int value = -1; Atom atom, actual_type; int actual_format; unsigned long nitems, bytes_after; g_autofree unsigned char *buffer = NULL; atom = XInternAtom (manager_xrandr->xdisplay, "Backlight", False); XRRGetOutputProperty (manager_xrandr->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 (MetaMonitorManagerXrandr *manager_xrandr, MetaOutput *output) { Atom atom; xcb_connection_t *xcb_conn; g_autofree xcb_randr_query_output_property_reply_t *reply = NULL; atom = XInternAtom (manager_xrandr->xdisplay, "Backlight", False); xcb_conn = XGetXCBConnection (manager_xrandr->xdisplay); reply = xcb_randr_query_output_property_reply (xcb_conn, xcb_randr_query_output_property (xcb_conn, (xcb_randr_output_t) output->winsys_id, (xcb_atom_t) atom), 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 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 guint8 * get_edid_property (Display *dpy, RROutput output, Atom atom, gsize *len) { unsigned char *prop; int actual_format; unsigned long nitems, bytes_after; Atom actual_type; guint8 *result; XRRGetOutputProperty (dpy, 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_memdup (prop, nitems); if (len) *len = nitems; } else { result = NULL; } XFree (prop); return result; } static GBytes * read_output_edid (MetaMonitorManagerXrandr *manager_xrandr, XID winsys_id) { Atom edid_atom; guint8 *result; gsize len; edid_atom = XInternAtom (manager_xrandr->xdisplay, "EDID", FALSE); result = get_edid_property (manager_xrandr->xdisplay, winsys_id, edid_atom, &len); if (!result) { edid_atom = XInternAtom (manager_xrandr->xdisplay, "EDID_DATA", FALSE); result = get_edid_property (manager_xrandr->xdisplay, 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 void output_get_tile_info (MetaMonitorManagerXrandr *manager_xrandr, MetaOutput *output) { Atom tile_atom; unsigned char *prop; unsigned long nitems, bytes_after; int actual_format; Atom actual_type; if (manager_xrandr->has_randr15 == FALSE) return; tile_atom = XInternAtom (manager_xrandr->xdisplay, "TILE", FALSE); XRRGetOutputProperty (manager_xrandr->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 gboolean output_get_hotplug_mode_update (MetaMonitorManagerXrandr *manager_xrandr, MetaOutput *output) { return output_get_property_exists (manager_xrandr, output, "hotplug_mode_update"); } static gint output_get_suggested_x (MetaMonitorManagerXrandr *manager_xrandr, MetaOutput *output) { gint val; if (output_get_integer_property (manager_xrandr, output, "suggested X", &val)) return val; return -1; } static gint output_get_suggested_y (MetaMonitorManagerXrandr *manager_xrandr, MetaOutput *output) { gint val; if (output_get_integer_property (manager_xrandr, output, "suggested Y", &val)) return val; return -1; } static MetaConnectorType connector_type_from_atom (MetaMonitorManagerXrandr *manager_xrandr, Atom atom) { Display *xdpy = manager_xrandr->xdisplay; if (atom == XInternAtom (xdpy, "HDMI", True)) return META_CONNECTOR_TYPE_HDMIA; if (atom == XInternAtom (xdpy, "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 (xdpy, "Panel", True)) return META_CONNECTOR_TYPE_LVDS; if (atom == XInternAtom (xdpy, "DVI", True) || atom == XInternAtom (xdpy, "DVI-I", True)) return META_CONNECTOR_TYPE_DVII; if (atom == XInternAtom (xdpy, "DVI-A", True)) return META_CONNECTOR_TYPE_DVIA; if (atom == XInternAtom (xdpy, "DVI-D", True)) return META_CONNECTOR_TYPE_DVID; if (atom == XInternAtom (xdpy, "DisplayPort", True)) return META_CONNECTOR_TYPE_DisplayPort; if (atom == XInternAtom (xdpy, "TV", True)) return META_CONNECTOR_TYPE_TV; if (atom == XInternAtom (xdpy, "TV-Composite", True)) return META_CONNECTOR_TYPE_Composite; if (atom == XInternAtom (xdpy, "TV-SVideo", True)) return META_CONNECTOR_TYPE_SVIDEO; /* Another set of mismatches. */ if (atom == XInternAtom (xdpy, "TV-SCART", True)) return META_CONNECTOR_TYPE_TV; if (atom == XInternAtom (xdpy, "TV-C4", True)) return META_CONNECTOR_TYPE_TV; return META_CONNECTOR_TYPE_Unknown; } static MetaConnectorType output_get_connector_type_from_prop (MetaMonitorManagerXrandr *manager_xrandr, MetaOutput *output) { Atom atom, actual_type, connector_type_atom; int actual_format; unsigned long nitems, bytes_after; g_autofree unsigned char *buffer = NULL; atom = XInternAtom (manager_xrandr->xdisplay, "ConnectorType", False); XRRGetOutputProperty (manager_xrandr->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 (manager_xrandr, connector_type_atom); } static MetaConnectorType output_get_connector_type_from_name (MetaMonitorManagerXrandr *manager_xrandr, 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 (MetaMonitorManagerXrandr *manager_xrandr, 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 (manager_xrandr, 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 (manager_xrandr, output); if (ret != META_CONNECTOR_TYPE_Unknown) return ret; return META_CONNECTOR_TYPE_Unknown; } static void output_get_modes (MetaMonitorManager *manager, MetaOutput *output, XRROutputInfo *xrandr_output) { guint j, k; guint n_actual_modes; output->modes = g_new0 (MetaCrtcMode *, xrandr_output->nmode); n_actual_modes = 0; for (j = 0; j < (guint)xrandr_output->nmode; j++) { for (k = 0; k < manager->n_modes; k++) { if (xrandr_output->modes[j] == (XID)manager->modes[k].mode_id) { output->modes[n_actual_modes] = &manager->modes[k]; 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 (MetaMonitorManager *manager, MetaOutput *output, XRROutputInfo *xrandr_output) { guint j, k; guint n_actual_crtcs; output->possible_crtcs = g_new0 (MetaCrtc *, xrandr_output->ncrtc); n_actual_crtcs = 0; for (j = 0; j < (unsigned) xrandr_output->ncrtc; j++) { for (k = 0; k < manager->n_crtcs; k++) { if ((XID) manager->crtcs[k].crtc_id == xrandr_output->crtcs[j]) { output->possible_crtcs[n_actual_crtcs] = &manager->crtcs[k]; n_actual_crtcs += 1; break; } } } output->n_possible_crtcs = n_actual_crtcs; output->crtc = NULL; for (j = 0; j < manager->n_crtcs; j++) { if ((XID) manager->crtcs[j].crtc_id == xrandr_output->crtc) { output->crtc = &manager->crtcs[j]; break; } } } static char * get_xmode_name (XRRModeInfo *xmode) { int width = xmode->width; int height = xmode->height; return g_strdup_printf ("%dx%d", width, height); } static void meta_monitor_manager_xrandr_read_current (MetaMonitorManager *manager) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); XRRScreenResources *resources; RROutput primary_output; unsigned int i, j, k; unsigned int n_actual_outputs; int min_width, min_height; Screen *screen; BOOL dpms_capable, dpms_enabled; CARD16 dpms_state; if (manager_xrandr->resources) XRRFreeScreenResources (manager_xrandr->resources); manager_xrandr->resources = NULL; dpms_capable = DPMSCapable (manager_xrandr->xdisplay); if (dpms_capable && DPMSInfo (manager_xrandr->xdisplay, &dpms_state, &dpms_enabled) && dpms_enabled) { switch (dpms_state) { case DPMSModeOn: manager->power_save_mode = META_POWER_SAVE_ON; break; case DPMSModeStandby: manager->power_save_mode = META_POWER_SAVE_STANDBY; break; case DPMSModeSuspend: manager->power_save_mode = META_POWER_SAVE_SUSPEND; break; case DPMSModeOff: manager->power_save_mode = META_POWER_SAVE_OFF; break; default: manager->power_save_mode = META_POWER_SAVE_UNSUPPORTED; break; } } else { manager->power_save_mode = META_POWER_SAVE_UNSUPPORTED; } XRRGetScreenSizeRange (manager_xrandr->xdisplay, DefaultRootWindow (manager_xrandr->xdisplay), &min_width, &min_height, &manager_xrandr->max_screen_width, &manager_xrandr->max_screen_height); screen = ScreenOfDisplay (manager_xrandr->xdisplay, DefaultScreen (manager_xrandr->xdisplay)); /* This is updated because we called RRUpdateConfiguration below */ manager->screen_width = WidthOfScreen (screen); manager->screen_height = HeightOfScreen (screen); resources = XRRGetScreenResourcesCurrent (manager_xrandr->xdisplay, DefaultRootWindow (manager_xrandr->xdisplay)); if (!resources) return; manager_xrandr->resources = resources; manager->n_outputs = resources->noutput; manager->n_crtcs = resources->ncrtc; manager->n_modes = resources->nmode; manager->outputs = g_new0 (MetaOutput, manager->n_outputs); manager->modes = g_new0 (MetaCrtcMode, manager->n_modes); manager->crtcs = g_new0 (MetaCrtc, manager->n_crtcs); for (i = 0; i < (unsigned)resources->nmode; i++) { XRRModeInfo *xmode = &resources->modes[i]; MetaCrtcMode *mode; mode = &manager->modes[i]; 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); } for (i = 0; i < (unsigned)resources->ncrtc; i++) { XRRCrtcInfo *crtc; MetaCrtc *meta_crtc; crtc = XRRGetCrtcInfo (manager_xrandr->xdisplay, resources, resources->crtcs[i]); meta_crtc = &manager->crtcs[i]; meta_crtc->crtc_id = resources->crtcs[i]; meta_crtc->rect.x = crtc->x; meta_crtc->rect.y = crtc->y; meta_crtc->rect.width = crtc->width; meta_crtc->rect.height = crtc->height; meta_crtc->is_dirty = FALSE; meta_crtc->transform = meta_monitor_transform_from_xrandr (crtc->rotation); meta_crtc->all_transforms = meta_monitor_transform_from_xrandr_all (crtc->rotations); for (j = 0; j < (unsigned)resources->nmode; j++) { if (resources->modes[j].id == crtc->mode) { meta_crtc->current_mode = &manager->modes[j]; break; } } XRRFreeCrtcInfo (crtc); } primary_output = XRRGetOutputPrimary (manager_xrandr->xdisplay, DefaultRootWindow (manager_xrandr->xdisplay)); n_actual_outputs = 0; for (i = 0; i < (unsigned)resources->noutput; i++) { XRROutputInfo *xrandr_output; MetaOutput *output; xrandr_output = XRRGetOutputInfo (manager_xrandr->xdisplay, resources, resources->outputs[i]); if (!xrandr_output) continue; output = &manager->outputs[n_actual_outputs]; if (xrandr_output->connection != RR_Disconnected) { GBytes *edid; output->winsys_id = resources->outputs[i]; output->name = g_strdup (xrandr_output->name); edid = read_output_edid (manager_xrandr, output->winsys_id); meta_output_parse_edid (output, edid); g_bytes_unref (edid); output->width_mm = xrandr_output->mm_width; output->height_mm = xrandr_output->mm_height; output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN; output->hotplug_mode_update = output_get_hotplug_mode_update (manager_xrandr, output); output->suggested_x = output_get_suggested_x (manager_xrandr, output); output->suggested_y = output_get_suggested_y (manager_xrandr, output); output->connector_type = output_get_connector_type (manager_xrandr, output); output_get_tile_info (manager_xrandr, output); output_get_modes (manager, output, xrandr_output); output_get_crtcs (manager, 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 (j = 0; j < (unsigned) xrandr_output->nclone; j++) { output->possible_clones[j] = GINT_TO_POINTER (xrandr_output->clones[j]); } output->is_primary = ((XID) output->winsys_id == primary_output); output->is_presentation = output_get_presentation_xrandr (manager_xrandr, output); output->is_underscanning = output_get_underscanning_xrandr (manager_xrandr, output); output->supports_underscanning = output_get_supports_underscanning_xrandr (manager_xrandr, output); output_get_backlight_limits_xrandr (manager_xrandr, output); if (!(output->backlight_min == 0 && output->backlight_max == 0)) output->backlight = output_get_backlight_xrandr (manager_xrandr, output); else output->backlight = -1; if (output->n_modes == 0 || output->n_possible_crtcs == 0) meta_monitor_manager_clear_output (output); else n_actual_outputs++; } XRRFreeOutputInfo (xrandr_output); } manager->n_outputs = n_actual_outputs; /* Sort the outputs for easier handling in MetaMonitorConfig */ qsort (manager->outputs, manager->n_outputs, sizeof (MetaOutput), compare_outputs); /* Now fix the clones */ for (i = 0; i < manager->n_outputs; i++) { MetaOutput *output; output = &manager->outputs[i]; for (j = 0; j < output->n_possible_clones; j++) { RROutput clone = GPOINTER_TO_INT (output->possible_clones[j]); for (k = 0; k < manager->n_outputs; k++) { if (clone == (XID)manager->outputs[k].winsys_id) { output->possible_clones[j] = &manager->outputs[k]; break; } } } } } static GBytes * meta_monitor_manager_xrandr_read_edid (MetaMonitorManager *manager, MetaOutput *output) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); return read_output_edid (manager_xrandr, output->winsys_id); } 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 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 (); } static void output_set_presentation_xrandr (MetaMonitorManagerXrandr *manager_xrandr, MetaOutput *output, gboolean presentation) { Atom atom; int value = presentation; atom = XInternAtom (manager_xrandr->xdisplay, "_UKWM_PRESENTATION_OUTPUT", False); xcb_randr_change_output_property (XGetXCBConnection (manager_xrandr->xdisplay), (XID)output->winsys_id, atom, XCB_ATOM_CARDINAL, 32, XCB_PROP_MODE_REPLACE, 1, &value); } static void output_set_underscanning_xrandr (MetaMonitorManagerXrandr *manager_xrandr, MetaOutput *output, gboolean underscanning) { Atom prop, valueatom; const char *value; prop = XInternAtom (manager_xrandr->xdisplay, "underscan", False); value = underscanning ? "on" : "off"; valueatom = XInternAtom (manager_xrandr->xdisplay, value, False); xcb_randr_change_output_property (XGetXCBConnection (manager_xrandr->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) { uint32_t border_value; prop = XInternAtom (manager_xrandr->xdisplay, "underscan hborder", False); border_value = output->crtc->current_mode->width * 0.05; xcb_randr_change_output_property (XGetXCBConnection (manager_xrandr->xdisplay), (XID)output->winsys_id, prop, XCB_ATOM_INTEGER, 32, XCB_PROP_MODE_REPLACE, 1, &border_value); prop = XInternAtom (manager_xrandr->xdisplay, "underscan vborder", False); border_value = output->crtc->current_mode->height * 0.05; xcb_randr_change_output_property (XGetXCBConnection (manager_xrandr->xdisplay), (XID)output->winsys_id, prop, XCB_ATOM_INTEGER, 32, XCB_PROP_MODE_REPLACE, 1, &border_value); } } static gboolean xrandr_set_crtc_config (MetaMonitorManagerXrandr *manager_xrandr, gboolean save_timestamp, xcb_randr_crtc_t 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_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; xcb_conn = XGetXCBConnection (manager_xrandr->xdisplay); config_timestamp = manager_xrandr->resources->configTimestamp; cookie = xcb_randr_set_crtc_config (xcb_conn, 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; } if (save_timestamp) manager_xrandr->last_xrandr_set_timestamp = reply->timestamp; free (reply); return TRUE; } static gboolean is_crtc_assignment_changed (MetaCrtc *crtc, MetaCrtcInfo **crtc_infos, unsigned int n_crtc_infos) { unsigned int i; for (i = 0; i < n_crtc_infos; i++) { MetaCrtcInfo *crtc_info = crtc_infos[i]; unsigned int j; if (crtc_info->crtc != crtc) continue; if (crtc->current_mode != crtc_info->mode) return TRUE; if (crtc->rect.x != crtc_info->x) return TRUE; if (crtc->rect.y != crtc_info->y) return TRUE; if (crtc->transform != crtc_info->transform) return TRUE; for (j = 0; j < crtc_info->outputs->len; j++) { MetaOutput *output = ((MetaOutput**) crtc_info->outputs->pdata)[j]; if (output->crtc != crtc) return TRUE; } return FALSE; } return crtc->current_mode != NULL; } static gboolean is_output_assignment_changed (MetaOutput *output, MetaCrtcInfo **crtc_infos, unsigned int n_crtc_infos, MetaOutputInfo **output_infos, unsigned int n_output_infos) { 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; } if (!output_is_found) return output->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 == output->crtc) return FALSE; } } return TRUE; } static gboolean is_assignments_changed (MetaMonitorManager *manager, MetaCrtcInfo **crtc_infos, unsigned int n_crtc_infos, MetaOutputInfo **output_infos, unsigned int n_output_infos) { unsigned int i; for (i = 0; i < manager->n_crtcs; i++) { MetaCrtc *crtc = &manager->crtcs[i]; if (is_crtc_assignment_changed (crtc, crtc_infos, n_crtc_infos)) return TRUE; } for (i = 0; i < manager->n_outputs; i++) { MetaOutput *output = &manager->outputs[i]; if (is_output_assignment_changed (output, crtc_infos, n_crtc_infos, output_infos, n_output_infos)) return TRUE; } 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); unsigned i; int width, height, width_mm, height_mm; XGrabServer (manager_xrandr->xdisplay); /* First compute the new size of the screen (framebuffer) */ width = 0; height = 0; 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) continue; if (meta_monitor_transform_is_rotated (crtc_info->transform)) { width = MAX (width, crtc_info->x + crtc_info->mode->height); height = MAX (height, crtc_info->y + crtc_info->mode->width); } else { width = MAX (width, crtc_info->x + crtc_info->mode->width); height = MAX (height, crtc_info->y + crtc_info->mode->height); } } /* 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; if (crtc_info->mode == NULL || crtc->rect.x + crtc->rect.width > width || crtc->rect.y + crtc->rect.height > height) { xrandr_set_crtc_config (manager_xrandr, save_timestamp, (xcb_randr_crtc_t) crtc->crtc_id, XCB_CURRENT_TIME, 0, 0, XCB_NONE, XCB_RANDR_ROTATION_ROTATE_0, NULL, 0); crtc->rect.x = 0; crtc->rect.y = 0; crtc->rect.width = 0; crtc->rect.height = 0; crtc->current_mode = NULL; } } /* Disable CRTCs not mentioned in the list */ for (i = 0; i < manager->n_crtcs; i++) { MetaCrtc *crtc = &manager->crtcs[i]; if (crtc->is_dirty) { crtc->is_dirty = FALSE; continue; } if (crtc->current_mode == NULL) continue; xrandr_set_crtc_config (manager_xrandr, save_timestamp, (xcb_randr_crtc_t) crtc->crtc_id, XCB_CURRENT_TIME, 0, 0, XCB_NONE, XCB_RANDR_ROTATION_ROTATE_0, NULL, 0); crtc->rect.x = 0; crtc->rect.y = 0; crtc->rect.width = 0; crtc->rect.height = 0; crtc->current_mode = NULL; } g_assert (width > 0 && height > 0); /* 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) * 25.4 + 0.5; height_mm = (height / DPI_FALLBACK) * 25.4 + 0.5; XRRSetScreenSize (manager_xrandr->xdisplay, DefaultRootWindow (manager_xrandr->xdisplay), width, height, width_mm, height_mm); 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; mode = crtc_info->mode; n_output_ids = crtc_info->outputs->len; output_ids = g_new (xcb_randr_output_t, n_output_ids); for (j = 0; j < n_output_ids; j++) { MetaOutput *output; output = ((MetaOutput**)crtc_info->outputs->pdata)[j]; output->is_dirty = TRUE; output->crtc = crtc; output_ids[j] = output->winsys_id; } rotation = meta_monitor_transform_to_xrandr (crtc_info->transform); if (!xrandr_set_crtc_config (manager_xrandr, save_timestamp, (xcb_randr_crtc_t) crtc->crtc_id, XCB_CURRENT_TIME, crtc_info->x, crtc_info->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, crtc_info->x, crtc_info->y, crtc_info->transform); continue; } if (meta_monitor_transform_is_rotated (crtc_info->transform)) { width = mode->height; height = mode->width; } else { width = mode->width; height = mode->height; } crtc->rect.x = crtc_info->x; crtc->rect.y = crtc_info->y; crtc->rect.width = width; crtc->rect.height = height; crtc->current_mode = mode; crtc->transform = crtc_info->transform; } } for (i = 0; i < n_outputs; i++) { MetaOutputInfo *output_info = outputs[i]; MetaOutput *output = output_info->output; if (output_info->is_primary) { XRRSetOutputPrimary (manager_xrandr->xdisplay, DefaultRootWindow (manager_xrandr->xdisplay), (XID)output_info->output->winsys_id); } output_set_presentation_xrandr (manager_xrandr, output_info->output, output_info->is_presentation); if (output_get_supports_underscanning_xrandr (manager_xrandr, output_info->output)) output_set_underscanning_xrandr (manager_xrandr, output_info->output, output_info->is_underscanning); 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 (i = 0; i < manager->n_outputs; i++) { MetaOutput *output = &manager->outputs[i]; if (output->is_dirty) { output->is_dirty = FALSE; continue; } output->crtc = NULL; output->is_primary = FALSE; } 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_rebuild_derived (MetaMonitorManager *manager, MetaMonitorsConfig *config) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); g_clear_pointer (&manager_xrandr->supported_scales, g_free); meta_monitor_manager_rebuild_derived (manager, config); } 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) { meta_monitor_manager_xrandr_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) { /* * 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 (is_assignments_changed (manager, (MetaCrtcInfo **) crtc_infos->pdata, crtc_infos->len, (MetaOutputInfo **) output_infos->pdata, output_infos->len)) { apply_crtc_assignments (manager, TRUE, (MetaCrtcInfo **) crtc_infos->pdata, crtc_infos->len, (MetaOutputInfo **) output_infos->pdata, output_infos->len); } else { meta_monitor_manager_xrandr_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) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); Atom atom; int hw_value; hw_value = round ((double)value / 100.0 * output->backlight_max + output->backlight_min); atom = XInternAtom (manager_xrandr->xdisplay, "Backlight", False); xcb_randr_change_output_property (XGetXCBConnection (manager_xrandr->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 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_memdup (gamma->red, sizeof (unsigned short) * gamma->size); *green = g_memdup (gamma->green, sizeof (unsigned short) * gamma->size); *blue = g_memdup (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); } #ifdef HAVE_XRANDR15 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; if (manager_xrandr->has_randr15 == FALSE) 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; } 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 (manager_xrandr->has_randr15 == FALSE) 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) { XRRMonitorInfo *m; int n, i; if (manager_xrandr->has_randr15 == FALSE) return; /* delete any tiled monitors setup, as ukwm 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); } #endif 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 float meta_monitor_manager_xrandr_calculate_monitor_mode_scale (MetaMonitorManager *manager, MetaMonitor *monitor, MetaMonitorMode *monitor_mode) { return meta_monitor_calculate_mode_scale (monitor, monitor_mode); } static void add_supported_scale (GArray *supported_scales, float scale) { unsigned int i; for (i = 0; i < supported_scales->len; i++) { float supported_scale = g_array_index (supported_scales, float, i); if (scale == supported_scale) return; } g_array_append_val (supported_scales, scale); } static int compare_scales (gconstpointer a, gconstpointer b) { float f = *(float *) a - *(float *) b; if (f < 0) return -1; if (f > 0) return 1; return 0; } static void ensure_supported_monitor_scales (MetaMonitorManager *manager) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); MetaMonitorScalesConstraint constraints; GList *l; GArray *supported_scales; if (manager_xrandr->supported_scales) return; constraints = META_MONITOR_SCALES_CONSTRAINT_NO_FRAC; supported_scales = g_array_new (FALSE, FALSE, sizeof (float)); for (l = manager->monitors; l; l = l->next) { MetaMonitor *monitor = l->data; MetaMonitorMode *monitor_mode; float *monitor_scales; int n_monitor_scales; int i; monitor_mode = meta_monitor_get_preferred_mode (monitor); monitor_scales = meta_monitor_calculate_supported_scales (monitor, monitor_mode, constraints, &n_monitor_scales); for (i = 0; i < n_monitor_scales; i++) add_supported_scale (supported_scales, monitor_scales[i]); g_array_sort (supported_scales, compare_scales); } manager_xrandr->supported_scales = (float *) supported_scales->data; manager_xrandr->n_supported_scales = supported_scales->len; g_array_free (supported_scales, FALSE); } static float * meta_monitor_manager_xrandr_calculate_supported_scales (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode, int *n_supported_scales) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); ensure_supported_monitor_scales (manager); *n_supported_scales = manager_xrandr->n_supported_scales; return g_memdup (manager_xrandr->supported_scales, manager_xrandr->n_supported_scales * sizeof (float)); } static MetaMonitorManagerCapability meta_monitor_manager_xrandr_get_capabilities (MetaMonitorManager *manager) { return (META_MONITOR_MANAGER_CAPABILITY_MIRRORING | META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED); } 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); *max_width = manager_xrandr->max_screen_width; *max_height = manager_xrandr->max_screen_height; return TRUE; } static MetaLogicalMonitorLayoutMode meta_monitor_manager_xrandr_get_default_layout_mode (MetaMonitorManager *manager) { return META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL; } static void meta_monitor_manager_xrandr_init (MetaMonitorManagerXrandr *manager_xrandr) { MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); manager_xrandr->xdisplay = meta_backend_x11_get_xdisplay (backend); 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); manager_xrandr->has_randr15 = FALSE; XRRQueryVersion (manager_xrandr->xdisplay, &major_version, &minor_version); #ifdef HAVE_XRANDR15 if (major_version > 1 || (major_version == 1 && minor_version >= 5)) { manager_xrandr->has_randr15 = TRUE; manager_xrandr->tiled_monitor_atoms = g_hash_table_new (NULL, NULL); } meta_monitor_manager_xrandr_init_monitors (manager_xrandr); #endif } } static void meta_monitor_manager_xrandr_finalize (GObject *object) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (object); if (manager_xrandr->resources) XRRFreeScreenResources (manager_xrandr->resources); manager_xrandr->resources = NULL; g_hash_table_destroy (manager_xrandr->tiled_monitor_atoms); g_free (manager_xrandr->supported_scales); G_OBJECT_CLASS (meta_monitor_manager_xrandr_parent_class)->finalize (object); } 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; manager_class->read_current = meta_monitor_manager_xrandr_read_current; manager_class->read_edid = meta_monitor_manager_xrandr_read_edid; 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->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; #ifdef HAVE_XRANDR15 manager_class->tiled_monitor_added = meta_monitor_manager_xrandr_tiled_monitor_added; manager_class->tiled_monitor_removed = meta_monitor_manager_xrandr_tiled_monitor_removed; #endif 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); 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); is_hotplug = (manager_xrandr->resources->timestamp < manager_xrandr->resources->configTimestamp); is_our_configuration = (manager_xrandr->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_xrandr_rebuild_derived (manager, config); } return TRUE; } ukwm/src/backends/x11/meta-cursor-renderer-x11.h0000664000175000017500000000415013233511035020256 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_CURSOR_RENDERER_X11_H #define META_CURSOR_RENDERER_X11_H #include "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 */ ukwm/src/backends/x11/nested/0000775000175000017500000000000013254604521014721 5ustar fengfengukwm/src/backends/x11/nested/meta-backend-x11-nested.c0000664000175000017500000001534313233511035021267 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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" G_DEFINE_TYPE (MetaBackendX11Nested, meta_backend_x11_nested, META_TYPE_BACKEND_X11) static MetaRenderer * meta_backend_x11_nested_create_renderer (MetaBackend *backend) { return g_object_new (META_TYPE_RENDERER_X11_NESTED, NULL); } static MetaMonitorManager * meta_backend_x11_nested_create_monitor_manager (MetaBackend *backend) { return g_object_new (META_TYPE_MONITOR_MANAGER_DUMMY, 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_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_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_init (MetaBackendX11Nested *backend_x11_nested) { } static void meta_backend_x11_nested_class_init (MetaBackendX11NestedClass *klass) { MetaBackendClass *backend_class = META_BACKEND_CLASS (klass); MetaBackendX11Class *backend_x11_class = META_BACKEND_X11_CLASS (klass); 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_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; } ukwm/src/backends/x11/nested/meta-renderer-x11-nested.h0000664000175000017500000000271313233511035021510 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 */ ukwm/src/backends/x11/nested/meta-renderer-x11-nested.c0000664000175000017500000001651613233511035021511 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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-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; main_monitor = meta_logical_monitor_get_monitors (logical_monitor)->data; main_output = meta_monitor_get_main_output (main_monitor); /* * 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, main_output->crtc, logical_monitor->transform)) return META_MONITOR_TRANSFORM_NORMAL; else return logical_monitor->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); meta_renderer_set_legacy_view (renderer, legacy_view); } static MetaRendererView * meta_renderer_x11_nested_create_view (MetaRenderer *renderer, MetaLogicalMonitor *logical_monitor) { 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; 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; if (meta_monitor_transform_is_rotated (view_transform)) { width = logical_monitor->rect.height; height = logical_monitor->rect.width; } else { width = logical_monitor->rect.width; height = logical_monitor->rect.height; } width *= view_scale; 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; return g_object_new (META_TYPE_RENDERER_VIEW, "layout", &logical_monitor->rect, "framebuffer", COGL_FRAMEBUFFER (fake_onscreen), "offscreen", COGL_FRAMEBUFFER (offscreen), "transform", view_transform, "scale", view_scale, "logical-monitor", logical_monitor, NULL); } 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; } ukwm/src/backends/x11/nested/meta-backend-x11-nested.h0000664000175000017500000000232113233511035021264 0ustar fengfeng/* * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef META_BACKEND_X11_NESTED_H #define META_BACKEND_X11_NESTED_H #include #include "backends/x11/meta-backend-x11.h" #define META_TYPE_BACKEND_X11_NESTED (meta_backend_x11_nested_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaBackendX11Nested, meta_backend_x11_nested, META, BACKEND_X11_NESTED, MetaBackendX11) struct _MetaBackendX11NestedClass { MetaBackendX11Class parent_class; }; #endif /* META_BACKEND_X11_NESTED_H */ ukwm/src/backends/x11/nested/meta-cursor-renderer-x11-nested.c0000664000175000017500000000501313233511035023012 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #include "config.h" #include "backends/x11/nested/meta-cursor-renderer-x11-nested.h" #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; } ukwm/src/backends/x11/nested/meta-cursor-renderer-x11-nested.h0000664000175000017500000000257713233511035023033 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 */ ukwm/src/backends/x11/meta-stage-x11-nested.c0000664000175000017500000003017413233511035017520 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #include "config.h" #include "backends/x11/meta-stage-x11-nested.h" #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor.h" #include "backends/meta-renderer.h" #include "backends/x11/nested/meta-renderer-x11-nested.h" #include "clutter/clutter-ukwm.h" static ClutterStageWindowIface *clutter_stage_window_parent_iface = NULL; struct _MetaStageX11Nested { ClutterStageX11 parent; CoglPipeline *pipeline; }; static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface); G_DEFINE_TYPE_WITH_CODE (MetaStageX11Nested, meta_stage_x11_nested, CLUTTER_TYPE_STAGE_X11, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW, clutter_stage_window_iface_init)) typedef struct _ClutterStageX11View { 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_crtc (MetaMonitor *monitor, MetaMonitorMode *monitor_mode, MetaMonitorCrtcMode *monitor_crtc_mode, gpointer user_data, GError **error) { DrawCrtcData *data = user_data; ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (data->stage_nested); CoglFramebuffer *onscreen = COGL_FRAMEBUFFER (stage_x11->onscreen); CoglTexture *texture = data->texture; MetaLogicalMonitor *logical_monitor = data->logical_monitor; MetaOutput *output = monitor_crtc_mode->output; MetaCrtc *crtc = output->crtc; MetaRendererView *renderer_view = META_RENDERER_VIEW (data->view); MetaMonitorTransform view_transform; MetaMonitorTransform layout_transform = META_MONITOR_TRANSFORM_NORMAL; cairo_rectangle_int_t view_layout; CoglMatrix projection_matrix; CoglMatrix transform; float texture_width, texture_height; float sample_x, sample_y, sample_width, sample_height; int viewport_x, viewport_y; int viewport_width, viewport_height; float s_1, t_1, s_2, t_2; texture_width = cogl_texture_get_width (texture); texture_height = cogl_texture_get_height (texture); clutter_stage_view_get_layout (data->view, &view_layout); sample_x = crtc->rect.x - view_layout.x; sample_y = crtc->rect.y - view_layout.y; sample_width = crtc->rect.width; sample_height = crtc->rect.height; clutter_stage_view_get_offscreen_transformation_matrix (data->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; view_transform = meta_renderer_view_get_transform (renderer_view); if (view_transform == logical_monitor->transform) { switch (view_transform) { case META_MONITOR_TRANSFORM_NORMAL: case META_MONITOR_TRANSFORM_FLIPPED: layout_transform = META_MONITOR_TRANSFORM_NORMAL; break; case META_MONITOR_TRANSFORM_270: case META_MONITOR_TRANSFORM_FLIPPED_270: layout_transform = META_MONITOR_TRANSFORM_90; break; case META_MONITOR_TRANSFORM_180: case META_MONITOR_TRANSFORM_FLIPPED_180: layout_transform = META_MONITOR_TRANSFORM_180; break; case META_MONITOR_TRANSFORM_90: case META_MONITOR_TRANSFORM_FLIPPED_90: layout_transform = META_MONITOR_TRANSFORM_270; break; } } else { layout_transform = logical_monitor->transform; } meta_monitor_calculate_crtc_pos (monitor, monitor_mode, output, layout_transform, &viewport_x, &viewport_y); viewport_x += logical_monitor->rect.x; viewport_y += logical_monitor->rect.y; if (meta_monitor_transform_is_rotated (logical_monitor->transform)) { viewport_width = monitor_crtc_mode->crtc_mode->height; viewport_height = monitor_crtc_mode->crtc_mode->width; } else { viewport_width = monitor_crtc_mode->crtc_mode->width; viewport_height = monitor_crtc_mode->crtc_mode->height; } cogl_framebuffer_set_viewport (onscreen, viewport_x, viewport_y, viewport_width, viewport_height); cogl_framebuffer_draw_textured_rectangle (onscreen, data->stage_nested->pipeline, 0, 0, 1, 1, s_1, t_1, s_2, t_2); cogl_framebuffer_pop_matrix (onscreen); return TRUE; } static void draw_logical_monitor (MetaStageX11Nested *stage_nested, MetaLogicalMonitor *logical_monitor, CoglTexture *texture, ClutterStageView *view, cairo_rectangle_int_t *view_layout) { MetaMonitor *monitor; MetaMonitorMode *current_mode; cogl_pipeline_set_layer_wrap_mode (stage_nested->pipeline, 0, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); monitor = meta_logical_monitor_get_monitors (logical_monitor)->data; current_mode = meta_monitor_get_current_mode (monitor); meta_monitor_mode_foreach_crtc (monitor, current_mode, draw_crtc, &(DrawCrtcData) { .stage_nested = stage_nested, .texture = texture, .view = view, .logical_monitor = logical_monitor }, NULL); } static void meta_stage_x11_nested_finish_frame (ClutterStageWindow *stage_window) { MetaStageX11Nested *stage_nested = META_STAGE_X11_NESTED (stage_window); ClutterStageX11 *stage_x11 = CLUTTER_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); MetaLogicalMonitor *logical_monitor; cairo_rectangle_int_t view_layout; CoglFramebuffer *framebuffer; CoglTexture *texture; clutter_stage_view_get_layout (view, &view_layout); 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); logical_monitor = meta_renderer_view_get_logical_monitor (renderer_view); if (logical_monitor) { draw_logical_monitor (stage_nested, logical_monitor, texture, view, &view_layout); } else { MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); GList *logical_monitors; GList *k; logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); for (k = logical_monitors; k; k = k->next) { logical_monitor = k->data; draw_logical_monitor (stage_nested, logical_monitor, texture, view, &view_layout); } } } 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); ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); GList *l; /* Clutter still uses part of the deprecated stateful API of Cogl * (in particulart cogl_set_framebuffer). It means Cogl can keep an * internal reference to the onscreen object we rendered to. In the * case of foreign window, we want to avoid this, as we don't know * what's going to happen to that window. * * The following call sets the current Cogl framebuffer to a dummy * 1x1 one if we're unrealizing the current one, so Cogl doesn't * keep any reference to the foreign window. */ for (l = meta_renderer_get_views (renderer); l ;l = l->next) { ClutterStageView *view = l->data; CoglFramebuffer *framebuffer = clutter_stage_view_get_framebuffer (view); if (cogl_get_draw_framebuffer () == framebuffer) { _clutter_backend_reset_cogl_framebuffer (stage_cogl->backend); break; } } 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 (ClutterStageWindowIface *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; } ukwm/src/backends/x11/meta-idle-monitor-xsync.c0000664000175000017500000002474013220600404020267 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2013 Red Hat, Inc. * Copyright 2017 Tianjin KYLIN Information Technology Co., 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, 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 "meta-idle-monitor-xsync.h" #include "meta-idle-monitor-private.h" #include "meta-backend-x11.h" #include struct _MetaIdleMonitorXSync { MetaIdleMonitor parent; GHashTable *alarms; Display *display; XSyncCounter counter; XSyncAlarm user_active_alarm; }; struct _MetaIdleMonitorXSyncClass { MetaIdleMonitorClass parent_class; }; typedef struct { MetaIdleMonitorWatch base; XSyncAlarm xalarm; } MetaIdleMonitorWatchXSync; G_DEFINE_TYPE (MetaIdleMonitorXSync, meta_idle_monitor_xsync, META_TYPE_IDLE_MONITOR) static gint64 _xsyncvalue_to_int64 (XSyncValue value) { return ((guint64) XSyncValueHigh32 (value)) << 32 | (guint64) XSyncValueLow32 (value); } #define GUINT64_TO_XSYNCVALUE(value, ret) XSyncIntsToValue (ret, (value) & 0xFFFFFFFF, ((guint64)(value)) >> 32) static XSyncAlarm _xsync_alarm_set (MetaIdleMonitorXSync *monitor_xsync, XSyncTestType test_type, guint64 interval, gboolean want_events) { XSyncAlarmAttributes attr; XSyncValue delta; guint flags; flags = XSyncCACounter | XSyncCAValueType | XSyncCATestType | XSyncCAValue | XSyncCADelta | XSyncCAEvents; XSyncIntToValue (&delta, 0); attr.trigger.counter = monitor_xsync->counter; attr.trigger.value_type = XSyncAbsolute; attr.delta = delta; attr.events = want_events; GUINT64_TO_XSYNCVALUE (interval, &attr.trigger.wait_value); attr.trigger.test_type = test_type; return XSyncCreateAlarm (monitor_xsync->display, flags, &attr); } static void ensure_alarm_rescheduled (Display *dpy, XSyncAlarm alarm) { XSyncAlarmAttributes attr; /* Some versions of Xorg have an issue where alarms aren't * always rescheduled. Calling XSyncChangeAlarm, even * without any attributes, will reschedule the alarm. */ XSyncChangeAlarm (dpy, alarm, 0, &attr); } static void set_alarm_enabled (Display *dpy, XSyncAlarm alarm, gboolean enabled) { XSyncAlarmAttributes attr; attr.events = enabled; XSyncChangeAlarm (dpy, alarm, XSyncCAEvents, &attr); } static char * counter_name_for_device (int device_id) { if (device_id > 0) return g_strdup_printf ("DEVICEIDLETIME %d", device_id); return g_strdup ("IDLETIME"); } static XSyncCounter find_idletime_counter (MetaIdleMonitorXSync *monitor_xsync) { MetaIdleMonitor *monitor = META_IDLE_MONITOR (monitor_xsync); int i; int ncounters; XSyncSystemCounter *counters; XSyncCounter counter = None; char *counter_name; counter_name = counter_name_for_device (monitor->device_id); counters = XSyncListSystemCounters (monitor_xsync->display, &ncounters); for (i = 0; i < ncounters; i++) { if (counters[i].name != NULL && strcmp (counters[i].name, counter_name) == 0) { counter = counters[i].counter; break; } } XSyncFreeSystemCounterList (counters); g_free (counter_name); return counter; } static void init_xsync (MetaIdleMonitorXSync *monitor_xsync) { monitor_xsync->counter = find_idletime_counter (monitor_xsync); /* IDLETIME counter not found? */ if (monitor_xsync->counter == None) { g_warning ("IDLETIME counter not found\n"); return; } monitor_xsync->user_active_alarm = _xsync_alarm_set (monitor_xsync, XSyncNegativeTransition, 1, FALSE); } static void meta_idle_monitor_xsync_dispose (GObject *object) { MetaIdleMonitorXSync *monitor_xsync = META_IDLE_MONITOR_XSYNC (object); if (monitor_xsync->user_active_alarm != None) { XSyncDestroyAlarm (monitor_xsync->display, monitor_xsync->user_active_alarm); monitor_xsync->user_active_alarm = None; } g_clear_pointer (&monitor_xsync->alarms, g_hash_table_destroy); G_OBJECT_CLASS (meta_idle_monitor_xsync_parent_class)->dispose (object); } static void meta_idle_monitor_xsync_constructed (GObject *object) { MetaIdleMonitorXSync *monitor_xsync = META_IDLE_MONITOR_XSYNC (object); MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); monitor_xsync->display = meta_backend_x11_get_xdisplay (backend); init_xsync (monitor_xsync); G_OBJECT_CLASS (meta_idle_monitor_xsync_parent_class)->constructed (object); } static gint64 meta_idle_monitor_xsync_get_idletime (MetaIdleMonitor *monitor) { MetaIdleMonitorXSync *monitor_xsync = META_IDLE_MONITOR_XSYNC (monitor); XSyncValue value; if (!XSyncQueryCounter (monitor_xsync->display, monitor_xsync->counter, &value)) return -1; return _xsyncvalue_to_int64 (value); } static gboolean fire_watch_idle (gpointer data) { MetaIdleMonitorWatch *watch = data; watch->idle_source_id = 0; _meta_idle_monitor_watch_fire (watch); return FALSE; } static guint32 get_next_watch_serial (void) { static guint32 serial = 0; g_atomic_int_inc (&serial); return serial; } static void free_watch (gpointer data) { MetaIdleMonitorWatchXSync *watch_xsync = data; MetaIdleMonitorWatch *watch = (MetaIdleMonitorWatch *) watch_xsync; MetaIdleMonitor *monitor = watch->monitor; MetaIdleMonitorXSync *monitor_xsync = META_IDLE_MONITOR_XSYNC (monitor); g_object_ref (monitor); if (watch->idle_source_id) { g_source_remove (watch->idle_source_id); watch->idle_source_id = 0; } if (watch->notify != NULL) watch->notify (watch->user_data); if (watch_xsync->xalarm != monitor_xsync->user_active_alarm && watch_xsync->xalarm != None) { XSyncDestroyAlarm (monitor_xsync->display, watch_xsync->xalarm); g_hash_table_remove (monitor_xsync->alarms, (gpointer) watch_xsync->xalarm); } g_object_unref (monitor); g_slice_free (MetaIdleMonitorWatchXSync, watch_xsync); } static MetaIdleMonitorWatch * meta_idle_monitor_xsync_make_watch (MetaIdleMonitor *monitor, guint64 timeout_msec, MetaIdleMonitorWatchFunc callback, gpointer user_data, GDestroyNotify notify) { MetaIdleMonitorXSync *monitor_xsync = META_IDLE_MONITOR_XSYNC (monitor); MetaIdleMonitorWatchXSync *watch_xsync; MetaIdleMonitorWatch *watch; watch_xsync = g_slice_new0 (MetaIdleMonitorWatchXSync); watch = (MetaIdleMonitorWatch *) watch_xsync; 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 (monitor_xsync->user_active_alarm != None) { if (timeout_msec != 0) { watch_xsync->xalarm = _xsync_alarm_set (monitor_xsync, XSyncPositiveTransition, timeout_msec, TRUE); g_hash_table_add (monitor_xsync->alarms, (gpointer) watch_xsync->xalarm); if (meta_idle_monitor_get_idletime (monitor) > (gint64)timeout_msec) { watch->idle_source_id = g_idle_add (fire_watch_idle, watch); g_source_set_name_by_id (watch->idle_source_id, "[ukwm] fire_watch_idle"); } } else { watch_xsync->xalarm = monitor_xsync->user_active_alarm; set_alarm_enabled (monitor_xsync->display, monitor_xsync->user_active_alarm, TRUE); } } return watch; } static void meta_idle_monitor_xsync_class_init (MetaIdleMonitorXSyncClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaIdleMonitorClass *idle_monitor_class = META_IDLE_MONITOR_CLASS (klass); object_class->dispose = meta_idle_monitor_xsync_dispose; object_class->constructed = meta_idle_monitor_xsync_constructed; idle_monitor_class->get_idletime = meta_idle_monitor_xsync_get_idletime; idle_monitor_class->make_watch = meta_idle_monitor_xsync_make_watch; } static void meta_idle_monitor_xsync_init (MetaIdleMonitorXSync *monitor_xsync) { MetaIdleMonitor *monitor = META_IDLE_MONITOR (monitor_xsync); monitor->watches = g_hash_table_new_full (NULL, NULL, NULL, free_watch); monitor_xsync->alarms = g_hash_table_new (NULL, NULL); } static void check_x11_watches (MetaIdleMonitor *monitor, XSyncAlarm alarm) { GList *node, *watch_ids; /* we get the keys and do explicit look ups in case * an early iteration of the loop ends up leading * to watches from later iterations getting invalidated */ 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); MetaIdleMonitorWatchXSync *watch; watch = g_hash_table_lookup (monitor->watches, GUINT_TO_POINTER (watch_id)); if (watch && watch->xalarm == alarm) _meta_idle_monitor_watch_fire ((MetaIdleMonitorWatch *) watch); } g_list_free (watch_ids); } void meta_idle_monitor_xsync_handle_xevent (MetaIdleMonitor *monitor, XSyncAlarmNotifyEvent *alarm_event) { MetaIdleMonitorXSync *monitor_xsync = META_IDLE_MONITOR_XSYNC (monitor); XSyncAlarm alarm; gboolean has_alarm; if (alarm_event->state != XSyncAlarmActive) return; alarm = alarm_event->alarm; has_alarm = FALSE; if (alarm == monitor_xsync->user_active_alarm) { set_alarm_enabled (monitor_xsync->display, alarm, FALSE); has_alarm = TRUE; } else if (g_hash_table_contains (monitor_xsync->alarms, (gpointer) alarm)) { ensure_alarm_rescheduled (monitor_xsync->display, alarm); has_alarm = TRUE; } if (has_alarm) check_x11_watches (monitor, alarm); } ukwm/src/backends/x11/meta-input-settings-x11.h0000664000175000017500000000377113220600404020135 0ustar fengfeng/* -*- 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 "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 */ ukwm/src/backends/x11/meta-backend-x11.c0000664000175000017500000005326013233511035016525 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include #include #include "meta-backend-x11.h" #include #include #include #include #include #include #include "meta-idle-monitor-xsync.h" #include "backends/meta-stage.h" #include "backends/x11/meta-clutter-backend-x11.h" #include "backends/x11/meta-renderer-x11.h" #include "meta/meta-cursor-tracker.h" #include #include "display-private.h" #include "compositor/compositor-private.h" #include "backends/meta-dnd-private.h" struct _MetaBackendX11Private { /* The host X11 display */ Display *xdisplay; xcb_connection_t *xcb; GSource *source; int xsync_event_base; int xsync_error_base; 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 handle_alarm_notify (MetaBackend *backend, XEvent *event) { meta_backend_foreach_device_monitor (backend, (GFunc) meta_idle_monitor_xsync_handle_xevent, event); } 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 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 != CurrentTime) { if (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; } /* Update the internal latest evtime, for any possible later use */ 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; if (event->evtype != XI_DeviceChanged) return; device_changed = (XIDeviceChangedEvent *) event; if (device_changed->reason != XISlaveSwitch) return; meta_backend_update_last_device (META_BACKEND (x11), device_changed->sourceid); } /* 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: 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; XGetEventData (priv->xdisplay, &event->xcookie); { MetaDisplay *display = meta_get_display (); if (display) { MetaCompositor *compositor = display->compositor; if (meta_plugin_manager_xevent_filter (compositor->plugin_mgr, event)) bypass_clutter = TRUE; if (meta_dnd_handle_xdnd_event (backend, compositor, display, 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, 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 XkbNewKeyboardNotify: 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); clutter_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; 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"); 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); } static ClutterBackend * meta_backend_x11_create_clutter_backend (MetaBackend *backend) { return g_object_new (META_TYPE_CLUTTER_BACKEND_X11, NULL); } static MetaIdleMonitor * meta_backend_x11_create_idle_monitor (MetaBackend *backend, int device_id) { return g_object_new (META_TYPE_IDLE_MONITOR_XSYNC, "device-id", device_id, 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 != CurrentTime) timestamp = MAX (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); return (ret == Success); } static void meta_backend_x11_warp_pointer (MetaBackend *backend, int x, int y) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); XIWarpPointer (priv->xdisplay, META_VIRTUAL_CORE_POINTER_ID, None, meta_backend_x11_get_xwindow (x11), 0, 0, 0, 0, x, y); } 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) { /* TODO: Currently handled by gnome-settings-deamon */ } 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_class_init (MetaBackendX11Class *klass) { MetaBackendClass *backend_class = META_BACKEND_CLASS (klass); backend_class->create_clutter_backend = meta_backend_x11_create_clutter_backend; backend_class->post_init = meta_backend_x11_post_init; backend_class->create_idle_monitor = meta_backend_x11_create_idle_monitor; backend_class->grab_device = meta_backend_x11_grab_device; backend_class->ungrab_device = meta_backend_x11_ungrab_device; backend_class->warp_pointer = meta_backend_x11_warp_pointer; 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() is needed to use the "threaded swap wait" functionality * in Cogl - see meta_renderer_x11_create_cogl_renderer(). We call it here * to hopefully call it before any other use of XLib. */ XInitThreads(); clutter_x11_request_reset_on_video_memory_purge (); /* We do X11 event retrieval ourselves */ clutter_x11_disable_event_retrieval (); } 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 clutter_x11_get_stage_window (CLUTTER_STAGE (stage)); } ukwm/src/backends/x11/meta-monitor-manager-xrandr.h0000664000175000017500000000264113220600404021117 0ustar fengfeng/* -*- 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 "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) gboolean meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager, XEvent *event); #endif /* META_MONITOR_MANAGER_XRANDR_H */ ukwm/src/backends/x11/meta-barrier-x11.c0000664000175000017500000001357713233511035016573 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre * Jonas Ã…dahl */ /** * SECTION:barrier-x11 * @Title: MetaBarrierImplX11 * @Short_Description: Pointer barriers implementation for X11 */ #include "config.h" #ifdef HAVE_XI23 #include #include #include #include #include "backends/x11/meta-barrier-x11.h" #include "display-private.h" struct _MetaBarrierImplX11Private { MetaBarrier *barrier; PointerBarrier xbarrier; }; G_DEFINE_TYPE_WITH_PRIVATE (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); MetaBarrierImplX11Private *priv = meta_barrier_impl_x11_get_instance_private (self); return priv->xbarrier != 0; } static void _meta_barrier_impl_x11_release (MetaBarrierImpl *impl, MetaBarrierEvent *event) { MetaBarrierImplX11 *self = META_BARRIER_IMPL_X11 (impl); MetaBarrierImplX11Private *priv = meta_barrier_impl_x11_get_instance_private (self); MetaDisplay *display = priv->barrier->priv->display; if (META_DISPLAY_HAS_XINPUT_23 (display)) { XIBarrierReleasePointer (display->xdisplay, META_VIRTUAL_CORE_POINTER_ID, priv->xbarrier, event->event_id); } } static void _meta_barrier_impl_x11_destroy (MetaBarrierImpl *impl) { MetaBarrierImplX11 *self = META_BARRIER_IMPL_X11 (impl); MetaBarrierImplX11Private *priv = meta_barrier_impl_x11_get_instance_private (self); MetaDisplay *display = priv->barrier->priv->display; Display *dpy; if (display == NULL) return; dpy = display->xdisplay; if (!meta_barrier_is_active (priv->barrier)) return; XFixesDestroyPointerBarrier (dpy, priv->xbarrier); g_hash_table_remove (display->xids, &priv->xbarrier); priv->xbarrier = 0; } MetaBarrierImpl * meta_barrier_impl_x11_new (MetaBarrier *barrier) { MetaBarrierImplX11 *self; MetaBarrierImplX11Private *priv; 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); priv = meta_barrier_impl_x11_get_instance_private (self); priv->barrier = barrier; dpy = display->xdisplay; root = DefaultRootWindow (dpy); allowed_motion_dirs = meta_border_get_allows_directions (&barrier->priv->border); priv->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->xids, &priv->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_display_process_barrier_xevent (MetaDisplay *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 (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) { } #endif /* HAVE_XI23 */ ukwm/src/backends/x11/meta-idle-monitor-xsync.h0000664000175000017500000000422413220600404020267 0ustar fengfeng/* -*- 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_XSYNC_H #define META_IDLE_MONITOR_XSYNC_H #include #include #include #include #define META_TYPE_IDLE_MONITOR_XSYNC (meta_idle_monitor_xsync_get_type ()) #define META_IDLE_MONITOR_XSYNC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_IDLE_MONITOR_XSYNC, MetaIdleMonitorXSync)) #define META_IDLE_MONITOR_XSYNC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_IDLE_MONITOR_XSYNC, MetaIdleMonitorXSyncClass)) #define META_IS_IDLE_MONITOR_XSYNC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_IDLE_MONITOR_XSYNC)) #define META_IS_IDLE_MONITOR_XSYNC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_IDLE_MONITOR_XSYNC)) #define META_IDLE_MONITOR_XSYNC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_IDLE_MONITOR_XSYNC, MetaIdleMonitorXSyncClass)) typedef struct _MetaIdleMonitorXSync MetaIdleMonitorXSync; typedef struct _MetaIdleMonitorXSyncClass MetaIdleMonitorXSyncClass; GType meta_idle_monitor_xsync_get_type (void); void meta_idle_monitor_xsync_handle_xevent (MetaIdleMonitor *monitor, XSyncAlarmNotifyEvent *xevent); #endif /* META_IDLE_MONITOR_XSYNC_H */ ukwm/src/backends/x11/meta-renderer-x11.h0000664000175000017500000000245113233511035016745 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 */ ukwm/src/backends/meta-screen-cast-stream.h0000664000175000017500000000367213233511035017625 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * */ #ifndef META_SCREEN_CAST_STREAM_H #define META_SCREEN_CAST_STREAM_H #include #include "backends/meta-screen-cast-stream-src.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); }; 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); #endif /* META_SCREEN_CAST_STREAM_H */ ukwm/src/backends/meta-renderer-view.c0000664000175000017500000001432513220600404016666 0ustar fengfeng/* * Copyright (C) 2016 Red Hat Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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 . */ #include "config.h" #include "backends/meta-renderer-view.h" #include "backends/meta-renderer.h" #include "clutter/clutter-ukwm.h" enum { PROP_0, PROP_MONITOR_INFO, PROP_TRANSFORM, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; struct _MetaRendererView { ClutterStageViewCogl parent; MetaMonitorTransform transform; MetaLogicalMonitor *logical_monitor; }; G_DEFINE_TYPE (MetaRendererView, meta_renderer_view, CLUTTER_TYPE_STAGE_VIEW_COGL) MetaLogicalMonitor * meta_renderer_view_get_logical_monitor (MetaRendererView *view) { return view->logical_monitor; } 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_MONITOR_INFO: g_value_set_pointer (value, view->logical_monitor); break; 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_MONITOR_INFO: view->logical_monitor = g_value_get_pointer (value); break; 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_MONITOR_INFO] = g_param_spec_pointer ("logical-monitor", "MetaLogicalMonitor", "The logical monitor of the view", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); 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); } ukwm/src/backends/meta-renderer-view.h0000664000175000017500000000247413220600404016675 0ustar fengfeng/* * Copyright (C) 2016 Red Hat Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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 META_RENDERER_VIEW_H #define META_RENDERER_VIEW_H #include "backends/meta-monitor-manager-private.h" #include "clutter/clutter-ukwm.h" #define META_TYPE_RENDERER_VIEW (meta_renderer_view_get_type ()) G_DECLARE_FINAL_TYPE (MetaRendererView, meta_renderer_view, META, RENDERER_VIEW, ClutterStageViewCogl) MetaLogicalMonitor *meta_renderer_view_get_logical_monitor (MetaRendererView *view); MetaMonitorTransform meta_renderer_view_get_transform (MetaRendererView *view); #endif /* META_RENDERER_VIEW_H */ ukwm/src/backends/meta-egl.c0000664000175000017500000005626013233511035014670 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #include "config.h" #include "backends/meta-backend-private.h" #include "backends/meta-egl.h" #include "backends/meta-egl-ext.h" #include "meta/util.h" #include #include #include #include #include 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); } static gboolean extensions_string_has_extensions_valist (const char *extensions_str, char ***missing_extensions, char *first_extension, va_list var_args) { char **extensions; 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, char ***missing_extensions, 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 = 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; } gboolean meta_egl_choose_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; } /* * 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_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; } 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; } 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; } 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, char ***missing_extensions, 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 = 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; } ukwm/src/backends/meta-screen-cast-monitor-stream-src.h0000664000175000017500000000325513233511035022074 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 */ ukwm/src/backends/meta-screen-cast-session.c0000664000175000017500000002740113233511035020004 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015-2017 Red Hat Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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-screen-cast-monitor-stream.h" #include "backends/meta-screen-cast-stream.h" #define META_SCREEN_CAST_SESSION_DBUS_PATH "/org/ukui/ukwm/ScreenCast/Session" struct _MetaScreenCastSession { MetaDBusScreenCastSessionSkeleton parent; char *peer_name; MetaScreenCastSessionType session_type; char *object_path; GList *streams; }; 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)) 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; } 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)); g_object_unref (session); } char * meta_screen_cast_session_get_object_path (MetaScreenCastSession *session) { return session->object_path; } 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 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; 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; } stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); monitor_stream = meta_screen_cast_monitor_stream_new (connection, monitor_manager, monitor, stage, &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); if (!check_permission (session, invocation)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Permission denied"); return TRUE; } g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Recording a window not yet supported"); 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->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_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; } ukwm/src/backends/meta-orientation-manager.c0000664000175000017500000001703313233511035020057 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "config.h" #include #include "backends/meta-orientation-manager.h" 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.ukui.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) { 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; if (g_settings_get_boolean (self->settings, ORIENTATION_LOCK_KEY)) 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; } ukwm/src/backends/meta-cursor-tracker-private.h0000664000175000017500000000460513220600404020533 0ustar fengfeng/* -*- 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 #include "meta-cursor.h" #include "meta-cursor-renderer.h" struct _MetaCursorTracker { GObject parent_instance; gboolean is_showing; 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. */ MetaCursorSprite *xfixes_cursor; }; struct _MetaCursorTrackerClass { GObjectClass parent_class; }; 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 ukwm/src/backends/meta-remote-desktop.h0000664000175000017500000000326213233511035017062 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 */ ukwm/src/backends/meta-backend-private.h0000664000175000017500000001601313233511035017155 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_BACKEND_PRIVATE_H #define META_BACKEND_PRIVATE_H #include #include #include #include #include "meta-cursor-renderer.h" #include "meta-monitor-manager-private.h" #include "meta-orientation-manager.h" #include "meta-input-settings-private.h" #include "backends/meta-egl.h" #include "backends/meta-pointer-constraint.h" #ifdef HAVE_REMOTE_DESKTOP #include "backends/meta-remote-desktop.h" #endif #include "backends/meta-renderer.h" #include "backends/meta-settings-private.h" #include "core/util-private.h" #define DEFAULT_XKB_RULES_FILE "evdev" #define DEFAULT_XKB_MODEL "pc105+inet" #define META_TYPE_BACKEND (meta_backend_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaBackend, meta_backend, META, BACKEND, GObject) struct _MetaBackendClass { GObjectClass parent_class; ClutterBackend * (* create_clutter_backend) (MetaBackend *backend); void (* post_init) (MetaBackend *backend); MetaIdleMonitor * (* create_idle_monitor) (MetaBackend *backend, int device_id); MetaMonitorManager * (* create_monitor_manager) (MetaBackend *backend); MetaCursorRenderer * (* create_cursor_renderer) (MetaBackend *backend); MetaRenderer * (* create_renderer) (MetaBackend *backend); 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 (* warp_pointer) (MetaBackend *backend, int x, int y); MetaLogicalMonitor * (* get_current_logical_monitor) (MetaBackend *backend); void (* set_keymap) (MetaBackend *backend, const char *layouts, const char *variants, const char *options); 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); gboolean (* get_relative_motion_deltas) (MetaBackend *backend, const ClutterEvent *event, double *dx, double *dy, double *dx_unaccel, double *dy_unaccel); void (* set_numlock) (MetaBackend *backend, gboolean numlock_state); }; void meta_init_backend (GType backend_gtype); ClutterBackend * meta_backend_get_clutter_backend (MetaBackend *backend); MetaIdleMonitor * meta_backend_get_idle_monitor (MetaBackend *backend, int device_id); void meta_backend_foreach_device_monitor (MetaBackend *backend, GFunc func, gpointer user_data); 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); MetaRenderer * meta_backend_get_renderer (MetaBackend *backend); MetaEgl * meta_backend_get_egl (MetaBackend *backend); MetaSettings * meta_backend_get_settings (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_warp_pointer (MetaBackend *backend, int x, int y); 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); void meta_backend_update_last_device (MetaBackend *backend, int device_id); gboolean meta_backend_get_relative_motion_deltas (MetaBackend *backend, const ClutterEvent *event, double *dx, double *dy, double *dx_unaccel, double *dy_unaccel); MetaPointerConstraint * meta_backend_get_client_pointer_constraint (MetaBackend *backend); void meta_backend_set_client_pointer_constraint (MetaBackend *backend, MetaPointerConstraint *constraint); ClutterBackend * meta_backend_get_clutter_backend (MetaBackend *backend); void meta_backend_monitors_changed (MetaBackend *backend); 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); #endif /* META_BACKEND_PRIVATE_H */ ukwm/src/backends/edid.h0000664000175000017500000001003013233510055014071 0ustar fengfeng/* 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 ukwm/src/backends/meta-logical-monitor.h0000664000175000017500000000705613233511035017224 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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 "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). */ glong 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) 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); 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); GList * meta_logical_monitor_get_monitors (MetaLogicalMonitor *logical_monitor); gboolean meta_logical_monitor_has_neighbor (MetaLogicalMonitor *logical_monitor, MetaLogicalMonitor *neighbor, MetaScreenDirection neighbor_dir); #endif /* META_LOGICAL_MONITOR_H */ ukwm/src/backends/meta-screen-cast-monitor-stream.h0000664000175000017500000000376413233511035021314 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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" #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 (GDBusConnection *connection, MetaMonitorManager *monitor_manager, MetaMonitor *monitor, ClutterStage *stage, 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 */ ukwm/src/backends/meta-renderer.h0000664000175000017500000000363513233511035015732 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jonas Ã…dahl */ #ifndef META_RENDERER_H #define META_RENDERER_H #include #include "cogl/cogl.h" #include "clutter/clutter-ukwm.h" #include "backends/meta-monitor-manager-private.h" #include "backends/meta-renderer-view.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); }; CoglRenderer * meta_renderer_create_cogl_renderer (MetaRenderer *renderer); void meta_renderer_rebuild_views (MetaRenderer *renderer); void meta_renderer_set_legacy_view (MetaRenderer *renderer, MetaRendererView *legacy_view); GList * meta_renderer_get_views (MetaRenderer *renderer); #endif /* META_RENDERER_H */ ukwm/src/backends/meta-idle-monitor-dbus.c0000664000175000017500000002320013220600404017435 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2013 Red Hat, Inc. * Copyright 2017 Tianjin KYLIN Information Technology Co., 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, 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 "meta-idle-monitor-dbus.h" #include #include "meta-dbus-idle-monitor.h" #include #include #include /* for meta_get_replace_current_wm () */ 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; } 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.ukui.ukwm.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-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_device_added (ClutterDeviceManager *device_manager, ClutterInputDevice *device, GDBusObjectManagerServer *manager) { MetaIdleMonitor *monitor; int device_id; char *path; device_id = clutter_input_device_get_device_id (device); monitor = meta_idle_monitor_get_for_device (device_id); path = g_strdup_printf ("/org/ukui/ukwm/IdleMonitor/Device%d", device_id); create_monitor_skeleton (manager, monitor, path); g_free (path); } static void on_device_removed (ClutterDeviceManager *device_manager, ClutterInputDevice *device, GDBusObjectManagerServer *manager) { int device_id; char *path; device_id = clutter_input_device_get_device_id (device); path = g_strdup_printf ("/org/ukui/ukwm/IdleMonitor/Device%d", device_id); g_dbus_object_manager_server_unexport (manager, path); g_free (path); } static void on_bus_acquired (GDBusConnection *connection, const char *name, gpointer user_data) { GDBusObjectManagerServer *manager; ClutterDeviceManager *device_manager; MetaIdleMonitor *monitor; GSList *devices, *iter; char *path; manager = g_dbus_object_manager_server_new ("/org/ukui/ukwm/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/ukui/ukwm/IdleMonitor/Core"); create_monitor_skeleton (manager, monitor, path); g_free (path); device_manager = clutter_device_manager_get_default (); devices = clutter_device_manager_list_devices (device_manager); for (iter = devices; iter; iter = iter->next) on_device_added (device_manager, iter->data, manager); g_slist_free (devices); g_signal_connect_object (device_manager, "device-added", G_CALLBACK (on_device_added), manager, 0); g_signal_connect_object (device_manager, "device-removed", G_CALLBACK (on_device_removed), manager, 0); 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.ukui.ukwm.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); } ukwm/src/backends/meta-monitor-manager-dummy.h0000664000175000017500000000374113220600404020345 0ustar fengfeng/* -*- 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 "meta-monitor-manager-private.h" #define META_TYPE_MONITOR_MANAGER_DUMMY (meta_monitor_manager_dummy_get_type ()) #define META_MONITOR_MANAGER_DUMMY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_MONITOR_MANAGER_DUMMY, MetaMonitorManagerDummy)) #define META_MONITOR_MANAGER_DUMMY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_MONITOR_MANAGER_DUMMY, MetaMonitorManagerDummyClass)) #define META_IS_MONITOR_MANAGER_DUMMY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_MONITOR_MANAGER_DUMMY)) #define META_IS_MONITOR_MANAGER_DUMMY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_MONITOR_MANAGER_DUMMY)) #define META_MONITOR_MANAGER_DUMMY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_MONITOR_MANAGER_DUMMY, MetaMonitorManagerDummyClass)) typedef struct _MetaMonitorManagerDummyClass MetaMonitorManagerDummyClass; typedef struct _MetaMonitorManagerDummy MetaMonitorManagerDummy; GType meta_monitor_manager_dummy_get_type (void); #endif /* META_MONITOR_MANAGER_DUMMY_H */ ukwm/src/backends/meta-monitor-config-manager.h0000664000175000017500000001544113233511035020464 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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), } MetaMonitorsConfigFlag; struct _MetaMonitorsConfig { GObject parent; MetaMonitorsConfigKey *key; GList *logical_monitor_configs; GList *disabled_monitor_specs; MetaMonitorsConfigFlag flags; MetaLogicalMonitorLayoutMode layout_mode; }; #define META_TYPE_MONITORS_CONFIG (meta_monitors_config_get_type ()) G_DECLARE_FINAL_TYPE (MetaMonitorsConfig, meta_monitors_config, META, MONITORS_CONFIG, GObject) MetaMonitorConfigManager * meta_monitor_config_manager_new (MetaMonitorManager *monitor_manager); MetaMonitorConfigStore * meta_monitor_config_manager_get_store (MetaMonitorConfigManager *config_manager); gboolean meta_monitor_config_manager_assign (MetaMonitorManager *manager, MetaMonitorsConfig *config, GPtrArray **crtc_infos, GPtrArray **output_infos, GError **error); MetaMonitorsConfig * meta_monitor_config_manager_get_stored (MetaMonitorConfigManager *config_manager); MetaMonitorsConfig * meta_monitor_config_manager_create_linear (MetaMonitorConfigManager *config_manager); MetaMonitorsConfig * meta_monitor_config_manager_create_fallback (MetaMonitorConfigManager *config_manager); MetaMonitorsConfig * meta_monitor_config_manager_create_suggested (MetaMonitorConfigManager *config_manager); MetaMonitorsConfig * meta_monitor_config_manager_create_for_orientation (MetaMonitorConfigManager *config_manager, MetaMonitorTransform transform); MetaMonitorsConfig * meta_monitor_config_manager_create_for_rotate_monitor (MetaMonitorConfigManager *config_manager); MetaMonitorsConfig * meta_monitor_config_manager_create_for_switch_config (MetaMonitorConfigManager *config_manager, MetaMonitorSwitchConfigType config_type); void meta_monitor_config_manager_set_current (MetaMonitorConfigManager *config_manager, MetaMonitorsConfig *config); MetaMonitorsConfig * meta_monitor_config_manager_get_current (MetaMonitorConfigManager *config_manager); MetaMonitorsConfig * meta_monitor_config_manager_pop_previous (MetaMonitorConfigManager *config_manager); MetaMonitorsConfig * meta_monitor_config_manager_get_previous (MetaMonitorConfigManager *config_manager); void meta_monitor_config_manager_clear_history (MetaMonitorConfigManager *config_manager); void meta_monitor_config_manager_save_current (MetaMonitorConfigManager *config_manager); MetaMonitorsConfig * meta_monitors_config_new_full (GList *logical_monitor_configs, GList *disabled_monitors, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitorsConfigFlag flags); MetaMonitorsConfig * meta_monitors_config_new (MetaMonitorManager *monitor_manager, GList *logical_monitor_configs, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitorsConfigFlag flags); unsigned int meta_monitors_config_key_hash (gconstpointer config_key); gboolean meta_monitors_config_key_equal (gconstpointer config_key_a, gconstpointer config_key_b); void meta_monitors_config_key_free (MetaMonitorsConfigKey *config_key); void meta_logical_monitor_config_free (MetaLogicalMonitorConfig *logical_monitor_config); void meta_monitor_config_free (MetaMonitorConfig *monitor_config); gboolean meta_logical_monitor_configs_have_monitor (GList *logical_monitor_configs, MetaMonitorSpec *monitor_spec); gboolean meta_verify_monitor_mode_spec (MetaMonitorModeSpec *monitor_mode_spec, GError **error); gboolean meta_verify_monitor_spec (MetaMonitorSpec *monitor_spec, GError **error); gboolean meta_verify_monitor_config (MetaMonitorConfig *monitor_config, GError **error); gboolean meta_verify_logical_monitor_config (MetaLogicalMonitorConfig *logical_monitor_config, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitorManager *monitor_manager, GError **error); gboolean meta_verify_monitors_config (MetaMonitorsConfig *config, MetaMonitorManager *monitor_manager, GError **error); #endif /* META_MONITOR_CONFIG_MANAGER_H */ ukwm/src/backends/meta-cursor-renderer.h0000664000175000017500000000653713233511035017251 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_CURSOR_RENDERER_H #define META_CURSOR_RENDERER_H #include #include #ifdef HAVE_WAYLAND #include #endif #include #include "meta-cursor.h" #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); #ifdef HAVE_WAYLAND void (* realize_cursor_from_wl_buffer) (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite, struct wl_resource *buffer); #endif void (* realize_cursor_from_xcursor) (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite, XcursorImage *xc_image); }; 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); void meta_cursor_renderer_force_update (MetaCursorRenderer *renderer); MetaCursorSprite * meta_cursor_renderer_get_cursor (MetaCursorRenderer *renderer); ClutterRect meta_cursor_renderer_calculate_rect (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite); #ifdef HAVE_WAYLAND void meta_cursor_renderer_realize_cursor_from_wl_buffer (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite, struct wl_resource *buffer); #endif void meta_cursor_renderer_realize_cursor_from_xcursor (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite, XcursorImage *xc_image); void meta_cursor_renderer_emit_painted (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite); #endif /* META_CURSOR_RENDERER_H */ ukwm/src/backends/meta-settings-private.h0000664000175000017500000000437713233511035017440 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef META_SETTINGS_PRIVATE_H #define META_SETTINGS_PRIVATE_H #include #include "meta/meta-settings.h" #include "meta/types.h" typedef enum _MetaExperimentalFeature { META_EXPERIMENTAL_FEATURE_NONE = 0, META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER = (1 << 0), META_EXPERIMENTAL_FEATURE_SCREEN_CAST = (1 << 1), META_EXPERIMENTAL_FEATURE_REMOTE_DESKTOP = (1 << 2), } MetaExperimentalFeature; #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); gboolean meta_settings_is_experimental_feature_enabled (MetaSettings *settings, MetaExperimentalFeature feature); MetaExperimentalFeature meta_settings_get_experimental_features (MetaSettings *settings); void meta_settings_override_experimental_features (MetaSettings *settings); void meta_settings_enable_experimental_feature (MetaSettings *settings, MetaExperimentalFeature feature); #endif /* META_SETTINGS_PRIVATE_H */ ukwm/src/backends/meta-screen-cast-stream.c0000664000175000017500000002044613233511035017616 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * */ #include "config.h" #include "backends/meta-screen-cast-stream.h" #define META_SCREEN_CAST_STREAM_DBUS_PATH "/org/ukui/ukwm/ScreenCast/Stream" enum { PROP_0, PROP_CONNECTION, }; enum { CLOSED, N_SIGNALS }; static guint signals[N_SIGNALS]; typedef struct _MetaScreenCastStreamPrivate { GDBusConnection *connection; char *object_path; 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) { MetaDBusScreenCastStream *skeleton = META_DBUS_SCREEN_CAST_STREAM (stream); meta_dbus_screen_cast_stream_emit_pipewire_stream_added (skeleton, node_id); } 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; } 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_CONNECTION: priv->connection = g_value_get_object (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_CONNECTION: g_value_set_object (value, priv->connection); 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_CONNECTION, g_param_spec_object ("connection", "connection", "GDBus connection", G_TYPE_DBUS_CONNECTION, 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); } ukwm/src/backends/meta-screen-cast.c0000664000175000017500000002114513233511035016322 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015-2017 Red Hat Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * */ #include "config.h" #include "backends/meta-screen-cast.h" #include #include "backends/meta-backend-private.h" #include "backends/meta-screen-cast-session.h" #include "backends/meta-remote-desktop-session.h" #define META_SCREEN_CAST_DBUS_SERVICE "org.ukui.ukwm.ScreenCast" #define META_SCREEN_CAST_DBUS_PATH "/org/ukui/ukwm/ScreenCast" struct _MetaScreenCast { MetaDBusScreenCastSkeleton parent; int dbus_name_id; GList *sessions; MetaDbusSessionWatcher *session_watcher; }; 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); } static gboolean register_remote_desktop_screen_cast_session (MetaScreenCastSession *session, const char *remote_desktop_session_id, GError **error) { MetaBackend *backend = meta_get_backend (); 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; 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; } } 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 (MetaDbusSessionWatcher *session_watcher) { MetaScreenCast *screen_cast; screen_cast = g_object_new (META_TYPE_SCREEN_CAST, NULL); 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; } } 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; } ukwm/src/backends/meta-idle-monitor.c0000664000175000017500000002256613220600404016520 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2013 Red Hat, Inc. * Copyright 2017 Tianjin KYLIN Information Technology Co., 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, 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: Ukwm idle counter (similar to X's IDLETIME) */ #include "config.h" #include #include #include #include #include #include #include #include "meta-idle-monitor-private.h" #include "meta-idle-monitor-dbus.h" #include "meta-backend-private.h" G_STATIC_ASSERT(sizeof(unsigned long) == sizeof(gpointer)); enum { PROP_0, PROP_DEVICE_ID, PROP_LAST, }; static GParamSpec *obj_props[PROP_LAST]; G_DEFINE_TYPE (MetaIdleMonitor, meta_idle_monitor, G_TYPE_OBJECT) void _meta_idle_monitor_watch_fire (MetaIdleMonitorWatch *watch) { MetaIdleMonitor *monitor; guint id; gboolean is_user_active_watch; monitor = watch->monitor; g_object_ref (monitor); if (watch->idle_source_id) { g_source_remove (watch->idle_source_id); watch->idle_source_id = 0; } 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_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_ID: g_value_set_int (value, monitor->device_id); 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_ID: monitor->device_id = g_value_get_int (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_id: * * The device to listen to idletime on. */ obj_props[PROP_DEVICE_ID] = g_param_spec_int ("device-id", "Device ID", "The device to listen to idletime on", 0, 255, 0, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_DEVICE_ID, obj_props[PROP_DEVICE_ID]); } static void meta_idle_monitor_init (MetaIdleMonitor *monitor) { } /** * meta_idle_monitor_get_core: * * Returns: (transfer none): the #MetaIdleMonitor that tracks the server-global * idletime for all devices. To track device-specific idletime, * use meta_idle_monitor_get_for_device(). */ MetaIdleMonitor * meta_idle_monitor_get_core (void) { MetaBackend *backend = meta_get_backend (); return meta_backend_get_idle_monitor (backend, 0); } /** * meta_idle_monitor_get_for_device: * @device_id: the device to get the idle time for. * * Returns: (transfer none): a new #MetaIdleMonitor that tracks the * device-specific idletime for @device. To track server-global idletime * for all devices, use meta_idle_monitor_get_core(). */ MetaIdleMonitor * meta_idle_monitor_get_for_device (int device_id) { MetaBackend *backend = meta_get_backend (); return meta_backend_get_idle_monitor (backend, device_id); } static MetaIdleMonitorWatch * make_watch (MetaIdleMonitor *monitor, guint64 timeout_msec, MetaIdleMonitorWatchFunc callback, gpointer user_data, GDestroyNotify notify) { MetaIdleMonitorWatch *watch; watch = META_IDLE_MONITOR_GET_CLASS (monitor)->make_watch (monitor, timeout_msec, callback, user_data, notify); 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 META_IDLE_MONITOR_GET_CLASS (monitor)->get_idletime (monitor); } ukwm/src/backends/meta-monitor-manager-private.h0000664000175000017500000005046213220600404020666 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * \file screen-private.h Handling of monitor configuration * * Managing multiple monitors * This file contains structures and functions that handle * multiple monitors, including reading the current configuration * and available hardware, and applying it. * * This interface is private to ukwm, API users should look * at MetaScreen instead. */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * Copyright (C) 2013 Red Hat Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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, see . */ #ifndef META_MONITOR_MANAGER_PRIVATE_H #define META_MONITOR_MANAGER_PRIVATE_H #include #include #include #include "display-private.h" #include #include "stack-tracker.h" #include #include "meta-display-config-shared.h" #include "meta-dbus-display-config.h" #include "meta-cursor.h" 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 struct _MetaMonitorMode MetaMonitorMode; typedef struct _MetaCrtc MetaCrtc; typedef struct _MetaOutput MetaOutput; typedef struct _MetaCrtcMode MetaCrtcMode; typedef struct _MetaCrtcInfo MetaCrtcInfo; typedef struct _MetaOutputInfo MetaOutputInfo; typedef struct _MetaTileInfo MetaTileInfo; #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_MIRRORING = (1 << 0), META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE = (1 << 1), META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED = (1 << 2) } MetaMonitorManagerCapability; /* Equivalent to the 'method' enum in org.ukui.ukwm.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.ukui.ukwm.DisplayConfig */ typedef enum _MetaLogicalMonitorLayoutMode { META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL = 1, META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL = 2 } MetaLogicalMonitorLayoutMode; typedef enum { 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, } MetaMonitorTransform; /* 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; /* Same as KMS mode flags and X11 randr flags */ typedef enum { 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; 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; }; struct _MetaOutput { /* The CRTC driving this output, NULL if the output is not enabled */ MetaCrtc *crtc; /* The low-level ID of this output, used to apply back configuration */ glong winsys_id; char *name; char *vendor; char *product; char *serial; int width_mm; int height_mm; CoglSubpixelOrder subpixel_order; MetaConnectorType connector_type; 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; /* The low-level bits used to build the high-level info in MetaLogicalMonitor XXX: flags maybe? There is a lot of code that uses MonitorInfo->is_primary, but nobody uses MetaOutput yet */ 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; }; struct _MetaCrtc { glong crtc_id; MetaRectangle rect; MetaCrtcMode *current_mode; MetaMonitorTransform transform; unsigned int all_transforms; /* Only used to build the logical configuration from the HW one */ MetaLogicalMonitor *logical_monitor; /* Used when changing configuration */ gboolean is_dirty; /* Used by cursor renderer backend */ void *cursor_renderer_private; gpointer driver_private; GDestroyNotify driver_notify; }; struct _MetaCrtcMode { /* 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; }; /* * MetaCrtcInfo: * * A representation of a CRTC configuration, generated by * MetaMonitorConfigManager. */ struct _MetaCrtcInfo { MetaCrtc *crtc; MetaCrtcMode *mode; int x; int y; 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 { MetaDBusDisplayConfigSkeleton parent_instance; /* XXX: this structure is very badly packed, but I like the logical organization of fields */ gboolean in_init; unsigned int serial; MetaPowerSave power_save_mode; MetaLogicalMonitorLayoutMode layout_mode; int screen_width; int screen_height; /* Outputs refer to physical screens, CRTCs refer to stuff that can drive outputs (like encoders, but less tied to the HW), while logical_monitors refer to logical ones. */ MetaOutput *outputs; unsigned int n_outputs; MetaCrtcMode *modes; unsigned int n_modes; MetaCrtc *crtcs; unsigned int n_crtcs; GList *monitors; struct { MetaOutput *outputs; unsigned int n_outputs; MetaCrtcMode *modes; unsigned int n_modes; MetaCrtc *crtcs; unsigned int n_crtcs; GList *monitors; } pending_cleanup; GList *logical_monitors; MetaLogicalMonitor *primary_logical_monitor; int dbus_name_id; int persistent_timeout_id; MetaMonitorConfigManager *config_manager; GnomePnpIds *pnp_ids; UpClient *up_client; gulong experimental_features_changed_handler_id; MetaMonitorSwitchConfigType current_switch_config; }; struct _MetaMonitorManagerClass { MetaDBusDisplayConfigSkeletonClass parent_class; void (*read_current) (MetaMonitorManager *); char* (*get_edid_file) (MetaMonitorManager *, MetaOutput *); GBytes* (*read_edid) (MetaMonitorManager *, MetaOutput *); gboolean (*is_lid_closed) (MetaMonitorManager *); void (*ensure_initial_config) (MetaMonitorManager *); gboolean (*apply_monitors_config) (MetaMonitorManager *, MetaMonitorsConfig *, MetaMonitorsConfigMethod , GError **); 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 *, 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 *); }; void meta_monitor_manager_rebuild (MetaMonitorManager *manager, MetaMonitorsConfig *config); void meta_monitor_manager_rebuild_derived (MetaMonitorManager *manager, MetaMonitorsConfig *config); int meta_monitor_manager_get_num_logical_monitors (MetaMonitorManager *manager); 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, MetaScreenDirection 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); GList * meta_monitor_manager_get_monitors (MetaMonitorManager *manager); MetaOutput *meta_monitor_manager_get_outputs (MetaMonitorManager *manager, unsigned int *n_outputs); void meta_monitor_manager_get_resources (MetaMonitorManager *manager, MetaCrtcMode **modes, unsigned int *n_modes, MetaCrtc **crtcs, unsigned int *n_crtcs, MetaOutput **outputs, unsigned int *n_outputs); void meta_monitor_manager_get_screen_size (MetaMonitorManager *manager, int *width, int *height); 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); void meta_monitor_manager_read_current_state (MetaMonitorManager *manager); void meta_monitor_manager_on_hotplug (MetaMonitorManager *manager); gboolean meta_monitor_manager_get_monitor_matrix (MetaMonitorManager *manager, 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); MetaMonitorsConfig * meta_monitor_manager_ensure_configured (MetaMonitorManager *manager); void meta_monitor_manager_update_logical_state (MetaMonitorManager *manager, MetaMonitorsConfig *config); void meta_monitor_manager_update_logical_state_derived (MetaMonitorManager *manager, MetaMonitorsConfig *config); gboolean meta_monitor_manager_is_lid_closed (MetaMonitorManager *manager); 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, 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); 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); /* 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); } #endif /* META_MONITOR_MANAGER_PRIVATE_H */ ukwm/src/backends/meta-monitor-config-migration.h0000664000175000017500000000315613233511035021043 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef META_MONITOR_CONFIG_MIGRATION_H #define META_MONITOR_CONFIG_MIGRATION_H #include "backends/meta-monitor-manager-private.h" gboolean meta_migrate_old_monitors_config (MetaMonitorConfigStore *config_store, GFile *in_file, GError **error); gboolean meta_migrate_old_user_monitors_config (MetaMonitorConfigStore *config_store, GError **error); gboolean meta_finish_monitors_config_migration (MetaMonitorManager *monitor_manager, MetaMonitorsConfig *config, GError **error); #endif /* META_MONITOR_CONFIG_MIGRATION_H */ ukwm/src/backends/meta-screen-cast-monitor-stream-src.c0000664000175000017500000001343313233511035022066 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * */ #include "config.h" #include "backends/meta-screen-cast-monitor-stream-src.h" #include "backends/meta-backend-private.h" #include "backends/meta-screen-cast-monitor-stream.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor.h" #include "clutter/clutter.h" #include "clutter/clutter-ukwm.h" struct _MetaScreenCastMonitorStreamSrc { MetaScreenCastStreamSrc parent; gulong stage_painted_handler_id; }; G_DEFINE_TYPE (MetaScreenCastMonitorStreamSrc, meta_screen_cast_monitor_stream_src, META_TYPE_SCREEN_CAST_STREAM_SRC) 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); scale = logical_monitor->scale; *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 (ClutterActor *actor, MetaScreenCastMonitorStreamSrc *monitor_src) { MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_src); meta_screen_cast_stream_src_maybe_record_frame (src); } static void meta_screen_cast_monitor_stream_src_enable (MetaScreenCastStreamSrc *src) { MetaScreenCastMonitorStreamSrc *monitor_src = META_SCREEN_CAST_MONITOR_STREAM_SRC (src); ClutterStage *stage; stage = get_stage (monitor_src); monitor_src->stage_painted_handler_id = g_signal_connect_after (stage, "paint", G_CALLBACK (stage_painted), monitor_src); 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); ClutterStage *stage; stage = get_stage (monitor_src); g_signal_handler_disconnect (stage, monitor_src->stage_painted_handler_id); monitor_src->stage_painted_handler_id = 0; } static void meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src, uint8_t *data) { 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); } 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) { } 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_frame = meta_screen_cast_monitor_stream_src_record_frame; } ukwm/src/backends/meta-orientation-manager.h0000664000175000017500000000261213233511035020061 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef META_ORIENTATION_MANAGER_H #define META_ORIENTATION_MANAGER_H 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 */ ukwm/src/backends/meta-remote-desktop-session.c0000664000175000017500000004134013233511035020535 0ustar fengfeng/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015-2017 Red Hat Inc. * Copyright (C) 2017 Tianjin KYLIN Information Technology Co., 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * */ #include "config.h" #include "backends/meta-remote-desktop-session.h" #include #include #include "backends/meta-dbus-session-watcher.h" #include "backends/meta-screen-cast-session.h" #include "backends/native/meta-backend-native.h" #include "backends/x11/meta-backend-x11.h" #include "cogl/cogl.h" #include "meta/meta-backend.h" #include "meta/errors.h" #include "meta-dbus-remote-desktop.h" #define META_REMOTE_DESKTOP_SESSION_DBUS_PATH "/org/ukui/ukwm/RemoteDesktop/Session" struct _MetaRemoteDesktopSession { MetaDBusRemoteDesktopSessionSkeleton parent; char *peer_name; char *session_id; char *object_path; MetaScreenCastSession *screen_cast_session; gulong screen_cast_session_closed_handler_id; ClutterVirtualInputDevice *virtual_pointer; ClutterVirtualInputDevice *virtual_keyboard; }; 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)) static gboolean meta_remote_desktop_session_is_running (MetaRemoteDesktopSession *session) { return !!session->virtual_pointer; } static gboolean meta_remote_desktop_session_start (MetaRemoteDesktopSession *session, GError **error) { ClutterDeviceManager *device_manager = clutter_device_manager_get_default (); g_assert (!session->virtual_pointer && !session->virtual_keyboard); if (session->screen_cast_session) { if (!meta_screen_cast_session_start (session->screen_cast_session, error)) return FALSE; } session->virtual_pointer = clutter_device_manager_create_virtual_device (device_manager, CLUTTER_POINTER_DEVICE); session->virtual_keyboard = clutter_device_manager_create_virtual_device (device_manager, CLUTTER_KEYBOARD_DEVICE); return TRUE; } void meta_remote_desktop_session_close (MetaRemoteDesktopSession *session) { MetaDBusRemoteDesktopSession *skeleton = META_DBUS_REMOTE_DESKTOP_SESSION (session); if (session->screen_cast_session) { g_signal_handler_disconnect (session->screen_cast_session, session->screen_cast_session_closed_handler_id); 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); 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)); 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 handle_start (MetaDBusRemoteDesktopSession *skeleton, GDBusMethodInvocation *invocation) { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_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; } 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 (!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_keysym (MetaDBusRemoteDesktopSession *skeleton, GDBusMethodInvocation *invocation, unsigned int keysym, gboolean pressed) { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); ClutterKeyState state; 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 (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 (!check_permission (session, invocation)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Permission denied"); 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 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 (); } 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; 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 (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; } if (steps != -1 || steps != 1) g_warning ("Multiple steps at at once not yet implemented, treating as one."); /* * 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); 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_absolute (MetaDBusRemoteDesktopSession *skeleton, GDBusMethodInvocation *invocation, const char *stream_path, double x, double y) { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_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; } clutter_virtual_input_device_notify_absolute_motion (session->virtual_pointer, CLUTTER_CURRENT_TIME, x, y); meta_dbus_remote_desktop_session_complete_notify_pointer_motion_absolute (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_keysym = handle_notify_keyboard_keysym; iface->handle_notify_pointer_button = handle_notify_pointer_button; iface->handle_notify_pointer_axis_discrete = handle_notify_pointer_axis_discrete; iface->handle_notify_pointer_motion_absolute = handle_notify_pointer_motion_absolute; } 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_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; } ukwm/src/backends/meta-remote-desktop-session.h0000664000175000017500000000420313233511035020537 0ustar fengfeng/* -*- 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, 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) 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 */ ukwm/src/backends/meta-egl-ext.h0000664000175000017500000001172413220600404015462 0ustar fengfeng/* * 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 /* * 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 */ ukwm/src/org.freedesktop.login1.xml0000664000175000017500000000264213220600404016260 0ustar fengfeng ukwm/src/meta-enum-types.h.in0000664000175000017500000000100613220600404015046 0ustar fengfeng/*** BEGIN file-header ***/ #ifndef __META_ENUM_TYPES_H__ #define __META_ENUM_TYPES_H__ #include G_BEGIN_DECLS /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ /*** END file-production ***/ /*** BEGIN file-tail ***/ G_END_DECLS #endif /* !__UKWM_ENUM_TYPES_H__ */ /*** END file-tail ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type (void) G_GNUC_CONST; #define META_TYPE_@ENUMSHORT@ (@enum_name@_get_type()) /*** END value-header ***/ ukwm/COPYING0000664000175000017500000004325413220600403011514 0ustar fengfeng 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. ukwm/autogen.sh0000775000175000017500000000107513220606154012466 0ustar fengfeng#!/bin/sh # Run this to generate all the initial makefiles, etc. srcdir=`dirname $0` test -z "$srcdir" && srcdir=. REQUIRED_AUTOMAKE_VERSION=1.11 olddir="$(pwd)" cd "${srcdir}" (test -f configure.ac \ && test -d src) || { echo -n "**Error**: Directory "\`$srcdir\'" does not look like the" echo " top-level mutter directory" exit 1 } aclocal --install || exit 1 intltoolize --force --copy --automake || exit 1 autoreconf --verbose --force --install || exit 1 cd "${olddir}" if [ "$NOCONFIGURE" = "" ]; then "${srcdir}/configure" "$@" || exit 1 fi ukwm/Makefile.am0000664000175000017500000000031013220600403012477 0ustar fengfeng SUBDIRS = cogl clutter data src po doc ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} DISTCLEANFILES = \ intltool-extract \ intltool-merge \ intltool-update \ po/stamp-it \ po/.intltool-merge-cache ukwm/doc/0000775000175000017500000000000013254604521011232 5ustar fengfengukwm/doc/code-overview.txt0000664000175000017500000001712013220600404014537 0ustar fengfengThis 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 ukwm/doc/Makefile.am0000664000175000017500000000014713220600404013255 0ustar fengfengSUBDIRS = man EXTRA_DIST = dialogs.txt code-overview.txt \ how-to-get-focus-right.txt rationales.txt ukwm/doc/how-to-get-focus-right.txt0000664000175000017500000003414013220600404016204 0ustar fengfengTo 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. ukwm/doc/dialogs.txt0000664000175000017500000000152613220600404013406 0ustar fengfengDialogs 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. ukwm/doc/man/0000775000175000017500000000000013254604521012005 5ustar fengfengukwm/doc/man/Makefile.am0000664000175000017500000000005413220600404014025 0ustar fengfengman_MANS = ukwm.1 EXTRA_DIST = $(man_MANS) ukwm/doc/man/ukwm.10000664000175000017500000000505613233760466013070 0ustar fengfeng.\" 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 Ukwm 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 Ukwm \- Clutter based compositing GTK2 Window Manager .SH SYNOPSIS .B ukwm [\-\-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 ukwm\fP. .PP .\" TeX users may be more comfortable with the \fB\fP and .\" \fI\fP escape sequences to invode bold face and italics, .\" respectively. \fBukwm\fP is a minimal X window manager aimed at nontechnical users and is designed to integrate well with the GNOME desktop. \fBukwm\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 GNOME 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 \fBukwm\fP. Users are encouraged to change the GNOME 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 \fBukwm\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 ukwm-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 ukwm. ukwm/doc/rationales.txt0000664000175000017500000000574613220600404014135 0ustar fengfeng 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 ukwm/po/0000775000175000017500000000000013254604521011103 5ustar fengfengukwm/po/ug.po0000664000175000017500000023001313220600404012042 0ustar fengfeng# Uyghur translation for ukwm. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Gheyret Kenji,2010. # Sahran , 2010. # Zeper , 2010. # msgid "" msgstr "" "Project-Id-Version: ukwm\n" "POT-Creation-Date: 2013-03-31 13:47+0000\n" "PO-Revision-Date: 2013-04-06 18:40+0900\n" "Last-Translator: Gheyret Kenji \n" "Language-Team: Uyghur Computer Science Association \n" "Language: ug\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: ../src/50-ukwm-navigation.xml.in.h:1 msgid "Navigation" msgstr "يولباشچى" #: ../src/50-ukwm-navigation.xml.in.h:2 msgid "Move window to workspace 1" msgstr "كۆزنەكنى 1-خىزمەت رايونىغا يۆتكە" #: ../src/50-ukwm-navigation.xml.in.h:3 msgid "Move window to workspace 2" msgstr "كۆزنەكنى 2-خىزمەت رايونىغا يۆتكە" #: ../src/50-ukwm-navigation.xml.in.h:4 msgid "Move window to workspace 3" msgstr "كۆزنەكنى 3-خىزمەت رايونىغا يۆتكە" #: ../src/50-ukwm-navigation.xml.in.h:5 msgid "Move window to workspace 4" msgstr "كۆزنەكنى 4-خىزمەت رايونىغا يۆتكە" #: ../src/50-ukwm-navigation.xml.in.h:6 msgid "Move window one workspace to the left" msgstr "كۆزنەكنى سولدىكى خىزمەت رايونىغا يۆتكەش" #: ../src/50-ukwm-navigation.xml.in.h:7 msgid "Move window one workspace to the right" msgstr "كۆزنەكنى ئوڭدىكى خىزمەت رايونىغا يۆتكەش" #: ../src/50-ukwm-navigation.xml.in.h:8 msgid "Move window one workspace up" msgstr "كۆزنەكنى يۇقىرىدىكى خىزمەت رايونىغا يۆتكەش" #: ../src/50-ukwm-navigation.xml.in.h:9 msgid "Move window one workspace down" msgstr "كۆزنەكنى تۆۋەندىكى خىزمەت رايونىغا يۆتكەش" #: ../src/50-ukwm-navigation.xml.in.h:10 msgid "Switch applications" msgstr "پروگراممىلارنى ئالماشتۇرۇش" #: ../src/50-ukwm-navigation.xml.in.h:11 msgid "Switch windows" msgstr "كۆزنەكلەرنى ئالماشتۇرۇش" #: ../src/50-ukwm-navigation.xml.in.h:12 msgid "Switch windows of an application" msgstr "پروگراممىنىڭ كۆزنىكىنى ئالماشتۇرۇش" #: ../src/50-ukwm-navigation.xml.in.h:13 msgid "Switch system controls" msgstr "سىستÛما تىزگىنلىرىنى ئالماشتۇرۇش" #: ../src/50-ukwm-navigation.xml.in.h:14 msgid "Switch windows directly" msgstr "كۆزنەكلەرنى بىۋاسىتە ئالماشتۇرۇش" #: ../src/50-ukwm-navigation.xml.in.h:15 msgid "Switch windows of an app directly" msgstr "بىر پروگراممىنىڭ كۆزنەكلىرىنى بىۋاسىتە ئالماشتۇرۇش" #: ../src/50-ukwm-navigation.xml.in.h:16 msgid "Switch system controls directly" msgstr "سىستÛما تىزگىنلىرىنى بىۋاسىتە ئالماشتۇرۇش" #: ../src/50-ukwm-navigation.xml.in.h:17 msgid "Hide all normal windows" msgstr "بارلىق نورمال كۆزنەكلەرنى يوشۇرۇش" #: ../src/50-ukwm-navigation.xml.in.h:18 msgid "Switch to workspace 1" msgstr "1-خىزمەت رايونىغا ئالمىشىش" #: ../src/50-ukwm-navigation.xml.in.h:19 msgid "Switch to workspace 2" msgstr "2-خىزمەت رايونىغا ئالمىشىش" #: ../src/50-ukwm-navigation.xml.in.h:20 msgid "Switch to workspace 3" msgstr "3-خىزمەت رايونىغا ئالمىشىش" #: ../src/50-ukwm-navigation.xml.in.h:21 msgid "Switch to workspace 4" msgstr "4-خىزمەت رايونىغا ئالمىشىش" #: ../src/50-ukwm-navigation.xml.in.h:22 msgid "Move to workspace left" msgstr "سولدىكى خىزمەت رايونىغا يۆتكەش" #: ../src/50-ukwm-navigation.xml.in.h:23 msgid "Move to workspace right" msgstr "ئوڭدىكى خىزمەت رايونىغا يۆتكەش" #: ../src/50-ukwm-navigation.xml.in.h:24 msgid "Move to workspace above" msgstr "ئۇستىدىكى خىزمەت رايونىغا يۆتكەش" #: ../src/50-ukwm-navigation.xml.in.h:25 msgid "Move to workspace below" msgstr "تۆۋەندىكى خىزمەت رايونىغا يۆتكەش" #: ../src/50-ukwm-system.xml.in.h:1 msgid "System" msgstr "سىستÛما" #: ../src/50-ukwm-system.xml.in.h:2 msgid "Show the run command prompt" msgstr "بۇيرۇق قۇرىنى كۆرسىتىدۇ" #: ../src/50-ukwm-system.xml.in.h:3 msgid "Show the activities overview" msgstr "پائالىيەتلەرنىڭ قىسقىچە بايانىنى كۆرسىتىدۇ" #: ../src/50-ukwm-windows.xml.in.h:1 msgid "Windows" msgstr "كۆزنەكلەر" #: ../src/50-ukwm-windows.xml.in.h:2 msgid "Activate the window menu" msgstr "كۆزنەك تىزىملىكىنى ئاكتىپلاش" #: ../src/50-ukwm-windows.xml.in.h:3 msgid "Toggle fullscreen mode" msgstr "پۈتۈن ئÛكران شەكلىگە ئالمىشىش" #: ../src/50-ukwm-windows.xml.in.h:4 msgid "Toggle maximization state" msgstr "ئەڭ Ú†ÙˆÚ­ ھالەتكە ئالمىشىش" #: ../src/50-ukwm-windows.xml.in.h:5 msgid "Maximize window" msgstr "كۆزنەكنى چوڭايتىش" #: ../src/50-ukwm-windows.xml.in.h:6 msgid "Restore window" msgstr "كۆزنەكنى ئەسلىگە كەلتۈرۈش" #: ../src/50-ukwm-windows.xml.in.h:7 msgid "Toggle shaded state" msgstr "سايىلىك ھالەتكە ئالمىشىش" #: ../src/50-ukwm-windows.xml.in.h:8 msgid "Close window" msgstr "كۆزنەك ياپ" #: ../src/50-ukwm-windows.xml.in.h:9 msgid "Hide window" msgstr "كۆزنەكنى يوشۇرۇش" #: ../src/50-ukwm-windows.xml.in.h:10 msgid "Move window" msgstr "كۆزنەكنى يۆتكەش" #: ../src/50-ukwm-windows.xml.in.h:11 msgid "Resize window" msgstr "كۆزنەك چوڭلۇقىنى ئۆزگەرتىش" #: ../src/50-ukwm-windows.xml.in.h:12 msgid "Toggle window on all workspaces or one" msgstr "كۆزنەكنى بارلىق خىزمەت رايونى ياكى بىرىگىلا ئالماشتۇرۇش" #: ../src/50-ukwm-windows.xml.in.h:13 msgid "Raise window if covered, otherwise lower it" msgstr "كۆزنەك توسۇلۇپ قالغان بولسا كۆتۈرسۇن، بولمىسا پەسلەتسۇن" #: ../src/50-ukwm-windows.xml.in.h:14 msgid "Raise window above other windows" msgstr "كۆزنەكنى باشقا كۆزنەكنىڭ ئۈستىگە كۆتۈرۈش" #: ../src/50-ukwm-windows.xml.in.h:15 msgid "Lower window below other windows" msgstr "كۆزنەكنى باشقا كۆزنەكنىڭ ئاستىغا تۆۋەنلىتىش" #: ../src/50-ukwm-windows.xml.in.h:16 msgid "Maximize window vertically" msgstr "كۆزنەكنى تىك يۆنىلىشتە چوڭايتىش" #: ../src/50-ukwm-windows.xml.in.h:17 msgid "Maximize window horizontally" msgstr "كۆزنەكنى توغرا يۆنىلىشتە چوڭايتىش" #: ../src/50-ukwm-windows.xml.in.h:18 msgid "View split on left" msgstr "كۆزنەكنىڭ سول تەرىپىدە كۆرسەتسۇن" #: ../src/50-ukwm-windows.xml.in.h:19 msgid "View split on right" msgstr "كۆزنەكنىڭ ئوڭ تەرىپىدە كۆرسەتسۇن" #. 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:568 #, c-format msgid "" "Another compositing manager is already running on screen %i on display \"%s" "\"." msgstr "كۆرسەتكۈچ «%2$s» نىڭدىكى ئÛكران %1$i دا بۆلەك باشقۇرغۇچ ئىجرا قىلىنىۋاتىدۇ." #: ../src/compositor/meta-background.c:1064 msgid "background texture could not be created from file" msgstr "ھۆججەتتىن تەگلىك texture نى قۇرغىلى بولمايدۇ" #: ../src/core/bell.c:322 msgid "Bell event" msgstr "قوڭغۇراق ھادىسىسى" #: ../src/core/core.c:157 #, c-format msgid "Unknown window information request: %d" msgstr "نامەلۇم كۆزنەك ئۇچۇرى ئىلتىماسى:%d" #: ../src/core/delete.c:111 #, c-format msgid "“%s†is not responding." msgstr "%s نىڭدىن جاۋاب كەلمەيۋاتىدۇ." #: ../src/core/delete.c:113 msgid "Application is not responding." msgstr "پروگراممىدا ئىنكاس يوق." #: ../src/core/delete.c:118 msgid "" "You may choose to wait a short while for it to continue or force the " "application to quit entirely." msgstr "داۋاملاشتۇرۇش ئۈچۈن كۈتۈشنى ياكى پروگراممىنى تولۇق ئاخىرلاشتۇرۇش ئۈچۈن مەجبۇرىينى تاللىسىڭىز بولىدۇ." #: ../src/core/delete.c:125 msgid "_Wait" msgstr "كۈت(_W)" #: ../src/core/delete.c:125 msgid "_Force Quit" msgstr "مەجبۇرى ئاخىرلاشتۇر(_F)" #: ../src/core/display.c:401 #, c-format msgid "Missing %s extension required for compositing" msgstr "بىرىكتۈرۈش ئۈچۈن زۆرۈر بولغان ÙƒÛڭەيتىلمە %s يوق" #: ../src/core/display.c:493 #, c-format msgid "Failed to open X Window System display '%s'\n" msgstr "â€X كۆزنەك سىستÛمىسى كۆرسەتكۈچى ‹%s› نى ئÛچىش مەغلۇپ بولدى\n" #: ../src/core/keybindings.c:935 #, c-format msgid "" "Some other program is already using the key %s with modifiers %x as a " "binding\n" msgstr "باشقا پروگرامما %s كۇنۇپكىسى بىلەن سۈپەتلىگۈچى كۇنۇپكا %x نىڭ بىرىكمىسىنى ئىشلىتىۋاتىدۇ\n" #: ../src/core/keybindings.c:1135 #, c-format #| msgid "\"%s\" is not a valid value for focus attribute" msgid "\"%s\" is not a valid accelerator\n" msgstr "«%s» ئىناۋەتلىك ØªÛØ²Ù„ەتكۈچ ئەمەس\n" #: ../src/core/main.c:197 msgid "Disable connection to session manager" msgstr "ئەڭگىمە باشقۇرغۇچقا باغلىنىشنى ئىناۋەتسىز قىل" #: ../src/core/main.c:203 msgid "Replace the running window manager" msgstr "ئىجرا قىلىنىۋاتقان كۆزنەك باشقۇرغۇچنى ئالماشتۇر" #: ../src/core/main.c:209 msgid "Specify session management ID" msgstr "ئەڭگىمە باشقۇرغۇ ID سÛنى بەلگىلە" #: ../src/core/main.c:214 msgid "X Display to use" msgstr "ئىشلىتىدىغان X كۆرسەتكۈچى" #: ../src/core/main.c:220 msgid "Initialize session from savefile" msgstr "ساقلانغان ھۆججەتتىن ئەڭگىمەنى دەسلەپلەشتۈرۈش" #: ../src/core/main.c:226 msgid "Make X calls synchronous" msgstr "X نى قەدەمداش قىلىپ ئىشلەت" #: ../src/core/main.c:534 #, c-format msgid "Failed to scan themes directory: %s\n" msgstr "ئۆرنەكلەر مۇندەرىجىسىنى ئىزدەش مەغلۇپ بولدى: %s\n" #: ../src/core/main.c:550 #, c-format msgid "" "Could not find a theme! Be sure %s exists and contains the usual themes.\n" msgstr "بىرەر ئۆرنەك تÛپىلمىدى. %s نىڭ مەۋجۇتلۇقى Ú¾Û•Ù… ئىشلىتىشكە بولىدىغان ئۆرنەكنى ئۆز ئىچىگە ئالغانلىقىنى جەزملەڭ.\n" #: ../src/core/ukwm.c:40 #, c-format msgid "" "ukwm %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" msgstr "ukwm %s\n" "نەشر ھوقۇقى (C) 2001-%d Havoc Pennington, Red Hat, Inc., Û‹Û• باشقىلارغا تەۋە\n" "بۇ ھەقسىز Ø¯ÛØªØ§Ù„Ø› كۆچۈرۈش شەرتلىرىنى مەنبە كودىدىن كۆرۈڭ.\n" "مەيلى قانداق مەقسەتتە ئىشلەتمەڭ، Ú¾Ûچقانداق كاپالەت يوق؛\n" #: ../src/core/ukwm.c:54 msgid "Print version" msgstr "نەشرىنى باس" #: ../src/core/ukwm.c:60 msgid "Ukwm plugin to use" msgstr "ئىشلىتىدىغان Ukwm قىستۇرمىسى" #: ../src/core/prefs.c:1095 msgid "" "Workarounds for broken applications disabled. Some applications may not " "behave properly.\n" msgstr "بۇزۇلغان پروگراممىلارنى تۈزىتىش-ياخشىلاش ئىناۋەتسىز قىلىنغان. بەزى پروگراممىلار نورمال ئىشلىمەسلىكى مۇمكىن.\n" #: ../src/core/prefs.c:1170 #, c-format msgid "Could not parse font description \"%s\" from GSettings key %s\n" msgstr "GSettings ئاچقۇچى %s نىڭ تەركىبىدىكى Ùونت چۈشەندۈرۈشى «%s»نى تەھلىل قىلغىنى بولمىدى\n" #: ../src/core/prefs.c:1236 #, c-format msgid "" "\"%s\" found in configuration database is not a valid value for mouse button " "modifier\n" msgstr "سەپلىمە سانداندىن تÛپىلغان «%s» چاشقىنەك توپچىسىنىڭ سۈپەتلىگۈچىسى ئۈچۈن ئىناۋەتسىز\n" #: ../src/core/prefs.c:1788 #, c-format msgid "" "\"%s\" found in configuration database is not a valid value for keybinding " "\"%s\"\n" msgstr "سەپلىمە ساندىنىدىن تÛپىلغان «%s»، «%s» كۇنۇپكا باغلانمىسىنىڭ ئىناۋەتلىك قىممىتى ئەمەس\n" #: ../src/core/prefs.c:1887 #, c-format msgid "Workspace %d" msgstr "خىزمەت بوشلۇقى %d" #: ../src/core/screen.c:691 #, c-format msgid "Screen %d on display '%s' is invalid\n" msgstr "كۆرسەتكۈچ ‹%2$s› دىكى ئÛكران %1$d ئىناۋەتسىز\n" #: ../src/core/screen.c:707 #, c-format msgid "" "Screen %d on display \"%s\" already has a window manager; try using the --" "replace option to replace the current window manager.\n" msgstr "كۆرسەتكۈچ «%2$s» دىكى ئÛكران %1$d نىڭ كۆزنەك باشقۇرغۇسى بار؛ ھازىرقى كۆزنەك باشقۇرغۇنى ئالماشتۇرۇش ئۈچۈن --replace تاللانمىسىنى ئىشلىتىپ كۆرۈپ بÛقىڭ.\n" #: ../src/core/screen.c:734 #, c-format msgid "" "Could not acquire window manager selection on screen %d display \"%s\"\n" msgstr "كۆرسەتكۈچ «%2$s» نىڭدىكى ئÛكران %1$d دا كۆزنەك باشقۇرغۇنىڭ تاللانمىسىنى ئالغىلى بولمىدى\n" #: ../src/core/screen.c:812 #, c-format msgid "Screen %d on display \"%s\" already has a window manager\n" msgstr "«%2$s» دىكى %1$d ئÛكراندا بىر كۆزنەك باشقۇرغۇ بار\n" #: ../src/core/screen.c:998 #, c-format msgid "Could not release screen %d on display \"%s\"\n" msgstr "«%2$s» دىكى %1$d ئÛكراننى بوشاتقىلى بولمىدى\n" #: ../src/core/session.c:843 ../src/core/session.c:850 #, c-format msgid "Could not create directory '%s': %s\n" msgstr "مۇندەرىجە ‹%s› نى قۇرغىلى بولمىدى :%s\n" #: ../src/core/session.c:860 #, c-format msgid "Could not open session file '%s' for writing: %s\n" msgstr "ئەڭگىمە ھۆججىتى ‹%s› نى ÙŠÛØ²Ù‰Ø´ ئۈچۈن ئاچقىلى بولمىدى:%s\n" #: ../src/core/session.c:1001 #, c-format msgid "Error writing session file '%s': %s\n" msgstr "ئەڭگىمە ھۆججىتى ‹%s› نى ÙŠÛØ²Ù‰Ø´ØªØ§ خاتالىق كۆرۈلدى:%s\n" #: ../src/core/session.c:1006 #, c-format msgid "Error closing session file '%s': %s\n" msgstr "ئەڭگىمە ھۆججىتى ‹%s› نى تاقاشتا خاتالىق كۆرۈلدى:%s\n" #: ../src/core/session.c:1136 #, c-format msgid "Failed to parse saved session file: %s\n" msgstr "ئەڭگىمە ھۆججىتى تەھلىل قىلىش مەغلۇپ بولدى: %s\n" #: ../src/core/session.c:1185 #, c-format msgid " attribute seen but we already have the session ID" msgstr " دÛÚ¯Û•Ù† خاسلىق بايقالدى، بىراق بىزدە ئەڭگىمە ID سى بار ئىدى." #: ../src/core/session.c:1198 ../src/core/session.c:1273 #: ../src/core/session.c:1305 ../src/core/session.c:1377 #: ../src/core/session.c:1437 #, c-format msgid "Unknown attribute %s on <%s> element" msgstr "<%2$s> ئÛÙ„ÛÙ…Ûنتتا نامەلۇم خاسلىق %1$s بار" #: ../src/core/session.c:1215 #, c-format msgid "nested tag" msgstr " بىلەن گىرەلىشىش بەلگىسى" #: ../src/core/session.c:1457 #, c-format msgid "Unknown element %s" msgstr "نامەلۇم ئÛÙ„ÛÙ…Ûنت %s" #: ../src/core/session.c:1809 msgid "" "These windows do not support "save current setup" and will have to " "be restarted manually next time you log in." msgstr "بۇ كۆزنەكلەردە «ھازىرقى تەڭشەكنى ساقلاش» ئىقتىدارىنى ئىشلەتكىلى بولمايدۇ. ÙƒÛيىن كىرگەندە ÙŠÛ•Ù†Û• قوزغىتىڭ." #: ../src/core/util.c:84 #, c-format msgid "Failed to open debug log: %s\n" msgstr "سازلاش خاتىرىسىنى ئÛچىش مەغلۇپ بولدى:%s\n" #: ../src/core/util.c:94 #, c-format msgid "Failed to fdopen() log file %s: %s\n" msgstr "خاتىرە ھۆججىتى %s غا fdopen() مەشغۇلاتى قىلغىلى بولمىدى:%s\n" #: ../src/core/util.c:100 #, c-format msgid "Opened log file %s\n" msgstr "ئاچقان خاتىرە ھۆججەت %s\n" #: ../src/core/util.c:119 ../src/tools/ukwm-message.c:149 #, c-format msgid "Ukwm was compiled without support for verbose mode\n" msgstr "Ukwm تەرجىمە-تەھرىرلىگەندە تەپسىلات قوللاش ھالىتى قوشۇلمىغان\n" #: ../src/core/util.c:264 msgid "Window manager: " msgstr "كۆزنەك باشقۇرغۇ: " #: ../src/core/util.c:412 msgid "Bug in window manager: " msgstr "كۆزنەك باشقۇرغۇدىكى كەمتۈك: " #: ../src/core/util.c:443 msgid "Window manager warning: " msgstr "كۆزنەك باشقۇرغۇ ئاگاھلاندۇرۇشى: " #: ../src/core/util.c:471 msgid "Window manager error: " msgstr "كۆزنەك باشقۇرغۇ خاتالىقى: " #. first time through #: ../src/core/window.c:7596 #, c-format msgid "" "Window %s sets SM_CLIENT_ID on itself, instead of on the WM_CLIENT_LEADER " "window as specified in the ICCCM.\n" msgstr "كۆزنەك %s بەلگىلىمە ICCCM دا بەلگىلەنگەن WM_CLIENT_LEADER كۆزنەكنى ئەمەس SM_CLIENT_ID نى ئۆزى بەلگىلىۋÛلىپتۇ.\n" #. 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. #. #: ../src/core/window.c:8320 #, c-format 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 "كۆزنەك %s نىڭدا MWM بەلگىلەنگەن بولۇپ، بۇ كۆزنەك چوڭلۇقىنى ئۆزگەرتكىلى بولمايدۇ دÛÚ¯Û•Ù† مەنىدە. بىراق ئەڭ كىچىك چوڭلۇقى%d x %dØŒ Û‹Û• ئەڭ كىچىك چوڭلۇقى %d x %d قىلىپ بەلگىلىنىپتۇ. بۇنىڭ Ú¾Ûچقانداق ئەھمىيىتى يوق.\n" #: ../src/core/window-props.c:318 #, c-format msgid "Application set a bogus _NET_WM_PID %lu\n" msgstr "پروگرامما بىر ساختا _NET_WM_PID نى بەلگىلىدى%lu\n" #: ../src/core/window-props.c:434 #, c-format msgid "%s (on %s)" msgstr "%s (ھازىر %s نىڭ ئۈستىدە)" #: ../src/core/window-props.c:1517 #, c-format msgid "Invalid WM_TRANSIENT_FOR window 0x%lx specified for %s.\n" msgstr "%2$s Ú¯Û• بەلگىلەنگەن ئۈنۈمسىز WM_TRANSIENT_FOR كۆزنەك 0x%1$lx بولىدۇ.\n" #: ../src/core/window-props.c:1528 #, c-format msgid "WM_TRANSIENT_FOR window 0x%lx for %s would create loop.\n" msgstr "%2$s نىڭ WM_TRANSIENT_FOR كۆزنەك0x%1$lx دەۋرىيلىك قۇرۇشى مۇمكىن.\n" #: ../src/core/xprops.c:155 #, c-format 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 "كۆزنەك 0x%1$lx نىڭدا type %5$s format %6$d n_items %7$d دÛÚ¯Û•Ù† \n" "خاسلىقنىڭ %2$s كۆرسىتىلگەن، ئەمەلىيەتتە type %3$s format %4$d بولۇشى \n" "تەلەپ قىلىناتتى. بۇ ÙƒÛ†Ù¾ ھاللاردا ئەپنىڭ كەمتۈكى بولۇپ،\n" "كۆزنەك باشقۇرغۇنىڭ كەمتۈكى ئەمەس.\n" "كۆزنەك خاسلىقى title=\"%8$s\" class=\"%9$s\" name=\"%10$s\" .\n" #: ../src/core/xprops.c:411 #, c-format msgid "Property %s on window 0x%lx contained invalid UTF-8\n" msgstr "خاسلىق %s (كۆزنەك 0x%lx) دا ئىناۋەتسىز UTF-8 بار\n" #: ../src/core/xprops.c:494 #, c-format msgid "" "Property %s on window 0x%lx contained invalid UTF-8 for item %d in the list\n" msgstr "خاسلىق %s (كۆزنەك 0x%lx) دىكى تىزىمنىڭ %d تۈرى تەركىبىدە ئىناۋەتسىز UTF-8 بار\n" #: ../src/ukwm.desktop.in.h:1 ../src/ukwm-wm.desktop.in.h:1 msgid "Ukwm" msgstr "Ukwm" #: ../src/org.ukui.ukwm.gschema.xml.in.h:1 msgid "Modifier to use for extended window management operations" msgstr "ÙƒÛڭەيتىلگەن كۆزنەك باشقۇرۇش مەشغۇلاتىغا ئىشلىتىلىدىغان ئۆزگەرتىش" #: ../src/org.ukui.ukwm.gschema.xml.in.h:2 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 "بۇ كۇنۇپكا كۆرسەتكەن «قاپلا» بىر خىل ئارىلاش كۆزنەك چۈشەندۈرۈشى Û‹Û• قوللىنىشچان پروگرامما ئىجرا قىلىنىدىغان سىستÛمىنى كۆرسىتىدۇ. كۆڭۈلدىكى ئەھۋالدا يەككە ÙƒÙˆÙ…Ù¾ÙŠÛ‡ØªÛØ±Ø¯Ù‰ÙƒÙ‰ \"Windows key\" كۇنۇپكىسىنى ئىشلىتىشنى تەلەپ قىلىدۇ. بەلكىم كۆڭۈلدىكى ياكى بوش تىزىقنى ئىشلىتىدۇ." #: ../src/org.ukui.ukwm.gschema.xml.in.h:3 msgid "Attach modal dialogs" msgstr "قوشۇلغان مودÛÙ„ سۆزلەشكۈ" #: ../src/org.ukui.ukwm.gschema.xml.in.h:4 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 "true بولغاندا مودÛÙ„ سۆزلەشكۈ ئاتا كۆزنەكنىڭ ماۋزۇ بالداققا ÙŠÛپىشىپ كۆرۈنىدۇ ھەمدە ئاتا كۆزنەككە ئەگىشىپ يۆتكىلىدۇ، ئايرىم ماۋزۇ بالدىقى بولمايدۇ." #: ../src/org.ukui.ukwm.gschema.xml.in.h:5 msgid "Enable edge tiling when dropping windows on screen edges" msgstr "" #: ../src/org.ukui.ukwm.gschema.xml.in.h:6 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 "" #: ../src/org.ukui.ukwm.gschema.xml.in.h:7 msgid "Workspaces are managed dynamically" msgstr "خىزمەت رايونلىرى جانلىق باشقۇرۇلسۇن" #: ../src/org.ukui.ukwm.gschema.xml.in.h:8 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 "خىزمەت رايونلىرى جانلىق باشقۇرۇلامدۇ ياكى خىزمەت رايونلىرىنىڭ سانى مۇقىم بولامدۇ بەلگىلەيدۇ(خىزمەت رايونلىرىنىڭ سانى org.gnome.desktop.wm.preferences دÛÚ¯Û•Ù† ئاچقۇچتا بەلگىلىنىدۇ)" #: ../src/org.ukui.ukwm.gschema.xml.in.h:9 msgid "Workspaces only on primary" msgstr "ئاساسىي كۆزەتكۈچتىكى خىزمەت رايونىغىلا" #: ../src/org.ukui.ukwm.gschema.xml.in.h:10 msgid "" "Determines whether workspace switching should happen for windows on all " "monitors or only for windows on the primary monitor." msgstr "خىزمەت رايونى ئالماشتۇرۇشنىڭ ھەممە ئÛكراندىكى كۆزنەككىمۇ ياكى ئاساسىي ئÛكرانغىلا قارىتىلغانلىقىنى جەزملەيدۇ." #: ../src/org.ukui.ukwm.gschema.xml.in.h:11 msgid "No tab popup" msgstr "بەتكۈچ سەكرىمىسۇن" #: ../src/org.ukui.ukwm.gschema.xml.in.h:12 msgid "" "Determines whether the use of popup and highlight frame should be disabled " "for window cycling." msgstr "" #: ../src/org.ukui.ukwm.gschema.xml.in.h:13 msgid "Delay focus changes until the pointer stops moving" msgstr "" #: ../src/org.ukui.ukwm.gschema.xml.in.h:14 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 "" #: ../src/org.ukui.ukwm.gschema.xml.in.h:15 msgid "Draggable border width" msgstr "سۆرەشكە بولىدىغان گىرۋەكنىڭ كەڭلىكى" #: ../src/org.ukui.ukwm.gschema.xml.in.h:16 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 "سۆرەشكە بولىدىغان گىرۋەكنىڭ كەڭلىكى. ئۆرنەكتە كۆرسىتىلىۋاتقان گىرۋەكنىڭ كەڭلىكى بۇنىڭدىن كىچىك بولسا، كۆرسەتكىلى بولمايدىغان گىرۋەككە مۇشۇ قىممەتكە ماس ÙƒÛلىدىغان قىلىپ قىممەت قوشۇلىدۇ." #: ../src/org.ukui.ukwm.gschema.xml.in.h:17 msgid "Auto maximize nearly monitor sized windows" msgstr "" #: ../src/org.ukui.ukwm.gschema.xml.in.h:18 msgid "" "If enabled, new windows that are initially the size of the monitor " "automatically get maximized." msgstr "" #: ../src/org.ukui.ukwm.gschema.xml.in.h:19 msgid "Select window from tab popup" msgstr "سەكرەپ چىققان بەتكۈچتىن كۆزنەك تاللىسۇن" #: ../src/org.ukui.ukwm.gschema.xml.in.h:20 msgid "Cancel tab popup" msgstr "سەكرەپ چىققان بەتكۈچنى ئەمەلدىن قالدۇر" #: ../src/tools/ukwm-message.c:123 #, c-format msgid "Usage: %s\n" msgstr "ئىشلىتىش ئۇسۇلى: %s\n" #. Translators: Translate this string the same way as you do in libwnck! #: ../src/ui/menu.c:69 msgid "Mi_nimize" msgstr "كىچىكلەت(_N)" #. Translators: Translate this string the same way as you do in libwnck! #: ../src/ui/menu.c:71 msgid "Ma_ximize" msgstr "چوڭايت(_X)" #. Translators: Translate this string the same way as you do in libwnck! #: ../src/ui/menu.c:73 msgid "Unma_ximize" msgstr "چوڭايتما(_X)" #. Translators: Translate this string the same way as you do in libwnck! #: ../src/ui/menu.c:75 msgid "Roll _Up" msgstr "تۈر(_U)" #. Translators: Translate this string the same way as you do in libwnck! #: ../src/ui/menu.c:77 msgid "_Unroll" msgstr "تۈرمە(_U)" #. Translators: Translate this string the same way as you do in libwnck! #: ../src/ui/menu.c:79 msgid "_Move" msgstr "يۆتكە(_M)" #. Translators: Translate this string the same way as you do in libwnck! #: ../src/ui/menu.c:81 msgid "_Resize" msgstr "چوڭلۇقىنى ئۆزگەرت(_R)" #. Translators: Translate this string the same way as you do in libwnck! #: ../src/ui/menu.c:83 msgid "Move Titlebar On_screen" msgstr "ماۋزۇ بالدىقىنى ئÛكرانغا يۆتكە(_S)" #. separator #. Translators: Translate this string the same way as you do in libwnck! #: ../src/ui/menu.c:86 ../src/ui/menu.c:88 msgid "Always on _Top" msgstr "دائىم چوققىدا تۇرغۇز(_T)" #. Translators: Translate this string the same way as you do in libwnck! #: ../src/ui/menu.c:90 msgid "_Always on Visible Workspace" msgstr "ھەمىشە كۆرۈنىدىغان خىزمەت بوشلۇقىدا(_A)" #. Translators: Translate this string the same way as you do in libwnck! #: ../src/ui/menu.c:92 msgid "_Only on This Workspace" msgstr "مۇشۇ خىزمەت بوشلۇقىدىلا(_O)" #. Translators: Translate this string the same way as you do in libwnck! #: ../src/ui/menu.c:94 msgid "Move to Workspace _Left" msgstr "سولدىكى خىزمەت بوشلۇقىغا يۆتكە(_L)" #. Translators: Translate this string the same way as you do in libwnck! #: ../src/ui/menu.c:96 msgid "Move to Workspace R_ight" msgstr "ئوڭدىكى خىزمەت بوشلۇقىغا يۆتكە(_I)" #. Translators: Translate this string the same way as you do in libwnck! #: ../src/ui/menu.c:98 msgid "Move to Workspace _Up" msgstr "ئۈستىدىكى خىزمەت بوشلۇقىغا يۆتكە(_U)" #. Translators: Translate this string the same way as you do in libwnck! #: ../src/ui/menu.c:100 msgid "Move to Workspace _Down" msgstr "ئاستىدىكى خىزمەت بوشلۇقىغا يۆتكە(_D)" #. separator #. Translators: Translate this string the same way as you do in libwnck! #: ../src/ui/menu.c:104 msgid "_Close" msgstr "ياپ(_C)" #: ../src/ui/menu.c:204 #, c-format msgid "Workspace %d%n" msgstr "خىزمەت رايونى %d%n" #: ../src/ui/menu.c:214 #, c-format msgid "Workspace 1_0" msgstr "خىزمەت بوشلۇقى 1_0" #: ../src/ui/menu.c:216 #, c-format msgid "Workspace %s%d" msgstr "خىزمەت بوشلۇقى %s%d" #: ../src/ui/menu.c:397 msgid "Move to Another _Workspace" msgstr "باشقا خىزمەت بوشلۇقىغا يۆتكە(_W)" #. This is the text that should appear next to menu accelerators #. * that use the shift key. If the text on this key isn't typically #. * translated on keyboards used for your language, don't translate #. * this. #. #: ../src/ui/metaaccellabel.c:77 msgid "Shift" msgstr "Shift" #. This is the text that should appear next to menu accelerators #. * that use the control key. If the text on this key isn't typically #. * translated on keyboards used for your language, don't translate #. * this. #. #: ../src/ui/metaaccellabel.c:83 msgid "Ctrl" msgstr "Ctrl" #. This is the text that should appear next to menu accelerators #. * that use the alt key. If the text on this key isn't typically #. * translated on keyboards used for your language, don't translate #. * this. #. #: ../src/ui/metaaccellabel.c:89 msgid "Alt" msgstr "Alt" #. This is the text that should appear next to menu accelerators #. * that use the meta key. If the text on this key isn't typically #. * translated on keyboards used for your language, don't translate #. * this. #. #: ../src/ui/metaaccellabel.c:95 msgid "Meta" msgstr "Meta" #. This is the text that should appear next to menu accelerators #. * that use the super key. If the text on this key isn't typically #. * translated on keyboards used for your language, don't translate #. * this. #. #: ../src/ui/metaaccellabel.c:101 msgid "Super" msgstr "Super" #. This is the text that should appear next to menu accelerators #. * that use the hyper key. If the text on this key isn't typically #. * translated on keyboards used for your language, don't translate #. * this. #. #: ../src/ui/metaaccellabel.c:107 msgid "Hyper" msgstr "Hyper" #. This is the text that should appear next to menu accelerators #. * that use the mod2 key. If the text on this key isn't typically #. * translated on keyboards used for your language, don't translate #. * this. #. #: ../src/ui/metaaccellabel.c:113 msgid "Mod2" msgstr "Mod2" #. This is the text that should appear next to menu accelerators #. * that use the mod3 key. If the text on this key isn't typically #. * translated on keyboards used for your language, don't translate #. * this. #. #: ../src/ui/metaaccellabel.c:119 msgid "Mod3" msgstr "Mod3" #. This is the text that should appear next to menu accelerators #. * that use the mod4 key. If the text on this key isn't typically #. * translated on keyboards used for your language, don't translate #. * this. #. #: ../src/ui/metaaccellabel.c:125 msgid "Mod4" msgstr "Mod4" #. This is the text that should appear next to menu accelerators #. * that use the mod5 key. If the text on this key isn't typically #. * translated on keyboards used for your language, don't translate #. * this. #. #: ../src/ui/metaaccellabel.c:131 msgid "Mod5" msgstr "Mod5" #. Translators: This represents the size of a window. The first number is #. * the width of the window and the second is the height. #. #: ../src/ui/resizepopup.c:136 #, c-format msgid "%d x %d" msgstr "%d x %d" #: ../src/ui/theme.c:236 msgid "top" msgstr "چوققا" #: ../src/ui/theme.c:238 msgid "bottom" msgstr "ئاستى" #: ../src/ui/theme.c:240 msgid "left" msgstr "سول" #: ../src/ui/theme.c:242 msgid "right" msgstr "ئوڭ" #: ../src/ui/theme.c:270 #, c-format msgid "frame geometry does not specify \"%s\" dimension" msgstr "كاندۇكنىڭ Ú¯ÛØ¦ÙˆÙ…ÛØªÙ‰Ø±Ù‰ÙŠÙ‰Ù„ىك شەكلى «%s» ئۆلچەمنى ئىپادىلىمەيدۇ" #: ../src/ui/theme.c:289 #, c-format msgid "frame geometry does not specify dimension \"%s\" for border \"%s\"" msgstr "كاندۇكنىڭ Ú¯ÛØ¦ÙˆÙ…ÛØªÙ‰Ø±Ù‰ÙŠÙ‰Ù„ىك شەكلى گىرۋەك «%2$s» نىڭ ئۆلچىمى «%1$s» ئىپادىلىمەيدۇ" #: ../src/ui/theme.c:326 #, c-format msgid "Button aspect ratio %g is not reasonable" msgstr "توپچىنىڭ ئÛگىزلىك Û‹Û• كەڭلىك نىسبىتى %g مۇۋاپىق ئەمەس" #: ../src/ui/theme.c:338 #, c-format msgid "Frame geometry does not specify size of buttons" msgstr "كاندۇكنىڭ Ú¯ÛØ¦ÙˆÙ…ÛØªÙ‰Ø±Ù‰ÙŠÙ‰Ù„ىك شەكلى توپچىلارنىڭ چوڭلۇقىنى ئىپادىلىمەيدۇ" #: ../src/ui/theme.c:1051 #, c-format msgid "Gradients should have at least two colors" msgstr "تەدرىجىي ئۆزگىرىشتە ئاز دÛگەندە ئىككى خىل رەڭ بولۇش ÙƒÛØ±Û•Ùƒ" #: ../src/ui/theme.c:1203 #, c-format msgid "" "GTK custom color specification must have color name and fallback in " "parentheses, e.g. gtk:custom(foo,bar); could not parse \"%s\"" msgstr "GTK رەڭ ئۆلچىمىدە ھالەتتىن ÙƒÛيىن چوقۇم رەڭ ئاتى Û‹Û• زاپاس بولۇشى لازىم، مەسىلەن، gtk:custom(foo,bar) ھالەت؛ «%s» نى تەھلىل قىلالمايدۇ" #: ../src/ui/theme.c:1219 #, c-format msgid "" "Invalid character '%c' in color_name parameter of gtk:custom, only A-Za-z0-9-" "_ are valid" msgstr "gtk:custom نىڭ color_name Ù¾Ø§Ø±Ø§Ù…ÛØªÙ‰Ø±Ù‰Ø¯Ù‰ÙƒÙ‰ ئىناۋەتسىز ھەرپ '%c'ØŒ پەقەت A-Za-z0-9-_ نىلا ئىشلەتكىلى بولىدۇ" #: ../src/ui/theme.c:1233 #, c-format msgid "" "Gtk:custom format is \"gtk:custom(color_name,fallback)\", \"%s\" does not " "fit the format" msgstr "Gtk:custom نىڭ پىچىمى \"gtk:custom(color_name,fallback)\" بولۇپ، «%s» پىچىمغا توغرا كەلمەيدۇ" #: ../src/ui/theme.c:1278 #, c-format 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 "GTK رەڭ بەلگىلىمىسىنىڭ ھالىتى چوقۇم gtk:fg[NORMAL] نىڭدەك تىرناق ئىچىگە ئÛلىنىشى ÙƒÛØ±Û•ك؛ «%s» نى تەھلىل قىلغىلى بولمىدى." #: ../src/ui/theme.c:1292 #, c-format 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 "GTK رەڭ بەلگىلىمىسىنىڭ ھالىتىنىڭ ئارقىسىغا سول تىرناق ÙŠÛØ²Ù‰Ù„ىشى ÙƒÛØ±Û•Ùƒ. مەسىلەن gtk:fg[NORMAL] نىڭدەك بۇ يەردىكى «NORMAL» ھالەتنى بىلدۈرىدۇ؛ «%s» نى تەھلىل قىلغىلى بولمىدى." #: ../src/ui/theme.c:1303 #, c-format msgid "Did not understand state \"%s\" in color specification" msgstr "رەڭ بەلگىلىمىسىدىكى «%s» ھالەتنى چۈشەنگىلى بولمىدى" #: ../src/ui/theme.c:1316 #, c-format msgid "Did not understand color component \"%s\" in color specification" msgstr "رەڭ بەلگىلىمىسىدىكى «%s» رەڭ بۆلىكىنى چۈشەنگىلى بولمىدى" #: ../src/ui/theme.c:1345 #, c-format msgid "" "Blend format is \"blend/bg_color/fg_color/alpha\", \"%s\" does not fit the " "format" msgstr "بىرىكمە رەڭنىڭ Ùورماتى \"blend/bg_color/fg_color/alpha\"ØŒ «%s» بۇ پىچىمغا ماس كەلمىدى" #: ../src/ui/theme.c:1356 #, c-format msgid "Could not parse alpha value \"%s\" in blended color" msgstr "بىرىكمە رەڭدىكى Ø¦Ø§Ù„ÙØ§ قىممىتى «%s» نى تەھلىل قىلغىلى بولمىدى" #: ../src/ui/theme.c:1366 #, c-format msgid "Alpha value \"%s\" in blended color is not between 0.0 and 1.0" msgstr "بىرىكمە رەڭنىڭ Ø¦Ø§Ù„ÙØ§ قىممىتى «%s» نىڭ دائىرىسى 0.0 ~1.0 ئىچىدە ئەمەس" #: ../src/ui/theme.c:1413 #, c-format msgid "" "Shade format is \"shade/base_color/factor\", \"%s\" does not fit the format" msgstr "سايە پىچىمى «shade/base_color/factor»، «%s» بۇ پىچىمغا ماسلاشمىدى" #: ../src/ui/theme.c:1424 #, c-format msgid "Could not parse shade factor \"%s\" in shaded color" msgstr "سايە رەڭگىدىكى سايە ÙØ§ÙƒØªÙˆØ± «%s» نى تەھلىل قىلغىلى بولمىدى" #: ../src/ui/theme.c:1434 #, c-format msgid "Shade factor \"%s\" in shaded color is negative" msgstr "سايە رەڭگىدىكى سايە ÙØ§ÙƒØªÙˆØ±Ù‰ «%s» مەنپىي سان" #: ../src/ui/theme.c:1463 #, c-format msgid "Could not parse color \"%s\"" msgstr "رەڭ «%s» نى ئانالىز قىلغىلى بولمىدى" #: ../src/ui/theme.c:1780 #, c-format msgid "Coordinate expression contains character '%s' which is not allowed" msgstr "كوئوردÛنات ئىپادىسىنىڭ تەركىبىدە رۇخسەت قىلىنمىغان ھەرپ '‹%s› بار" #: ../src/ui/theme.c:1807 #, c-format msgid "" "Coordinate expression contains floating point number '%s' which could not be " "parsed" msgstr "كوئوردÛنات ئىپادىسىنىڭ تەركىبىدە تەھلىل قىلغىلى بولمايدىغان كەسىر سان ‹%s› بار" #: ../src/ui/theme.c:1821 #, c-format msgid "Coordinate expression contains integer '%s' which could not be parsed" msgstr "كوئوردÛنات ئىپادىسىنىڭ تەركىبىدە تەھلىل قىلغىلى بولمايدىغان پۈتۈن سان ‹%s› بار" #: ../src/ui/theme.c:1942 #, c-format msgid "" "Coordinate expression contained unknown operator at the start of this text: " "\"%s\"" msgstr "كوئوردÛنات ئىپادىسىنىڭ Ø¨ÛØ´Ù‰Ø¯Ø§ نامەلۇم ئەمەل بار: «%s»" #: ../src/ui/theme.c:1999 #, c-format msgid "Coordinate expression was empty or not understood" msgstr "كوئوردÛنات ئىپادىسى قۇرۇق ياكى چۈشىنىكسىز" #: ../src/ui/theme.c:2112 ../src/ui/theme.c:2122 ../src/ui/theme.c:2156 #, c-format msgid "Coordinate expression results in division by zero" msgstr "كوئوردÛنات ئىپادىسى 0 نى بۆلگۈچى قىلغان" #: ../src/ui/theme.c:2164 #, c-format msgid "" "Coordinate expression tries to use mod operator on a floating-point number" msgstr "كوئوردÛنات ئىپادىسى كەسىر سانغا mod ئەمىلىنى ئىشلەتمەكچى" #: ../src/ui/theme.c:2220 #, c-format msgid "" "Coordinate expression has an operator \"%s\" where an operand was expected" msgstr "كوئوردÛنات ئىپادىسىنىڭ تەركىبىدە سان ÙƒÛلىدىغان يەردە ئەمەل «%s» بار ئىكەن" #: ../src/ui/theme.c:2229 #, c-format msgid "Coordinate expression had an operand where an operator was expected" msgstr "كوئوردÛنات ئىپادىسىنىڭ تەركىبىدە ئەمەل ÙƒÛلىدىغان يەردە سان بار ئىكەن" #: ../src/ui/theme.c:2237 #, c-format msgid "Coordinate expression ended with an operator instead of an operand" msgstr "كوئوردÛنات ئىپادىسى سان بىلەن ئاياغلاشماي ئەمەل بىلەن ئاياغلاشقان" #: ../src/ui/theme.c:2247 #, c-format msgid "" "Coordinate expression has operator \"%c\" following operator \"%c\" with no " "operand in between" msgstr "كوئوردÛنات ئىپادىسىنىڭ تەركىبىدە ئەمەل «%2$c» نىڭ ئارقىسىدىن ئەمەل «%1$c» ÙƒÛلىپتۇ، ئارىلىقتا سان يوق ئىكەن" #: ../src/ui/theme.c:2398 ../src/ui/theme.c:2443 #, c-format msgid "Coordinate expression had unknown variable or constant \"%s\"" msgstr "كوئوردÛنات ئىپادىسىنىڭ تەركىبىدە نامەلۇم ئۆزگەرگۈچى ياكى تۇراقلىق سان «%s» بار ئىكەن" #: ../src/ui/theme.c:2497 #, c-format msgid "Coordinate expression parser overflowed its buffer." msgstr "كوئوردÛنات ئىپادىسىنى تەھلىل قىلىۋاتقاندا يىغلەك ØªÛØ´Ù‰Ù¾ كەتتى." #: ../src/ui/theme.c:2526 #, c-format msgid "Coordinate expression had a close parenthesis with no open parenthesis" msgstr "كوئوردÛنات ئىپادىسىنىڭ تەركىبىدىكى ÙŠÛپىلغان تىرناققا ماس ÙƒÛلىدىغان ئÛچىلغان تىرناق يوق" #: ../src/ui/theme.c:2590 #, c-format msgid "Coordinate expression had an open parenthesis with no close parenthesis" msgstr "كوئوردÛنات ئىپادىسىنىڭ تەركىبىدىكى ئÛچىلغان تىرناققا ماس ÙƒÛلىدىغان ÙŠÛپىلغان تىرناق يوق" #: ../src/ui/theme.c:2601 #, c-format msgid "Coordinate expression doesn't seem to have any operators or operands" msgstr "كوئوردÛنات ئىپادىسىنىڭ تەركىبىدە ئەمەل(قوشۇش، ئÛلىش...) ياكى سان يوق" #: ../src/ui/theme.c:2814 ../src/ui/theme.c:2834 ../src/ui/theme.c:2854 #, c-format msgid "Theme contained an expression that resulted in an error: %s\n" msgstr "ئۆرنەك تەركىبىدە خاتالىق چىقىرىدىغان ئىپادە بار: %s\n" #: ../src/ui/theme.c:4500 #, c-format msgid "" "