diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
commit | f8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch) | |
tree | 26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Additions/x11 | |
parent | Initial commit. (diff) | |
download | virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.zip |
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Additions/x11')
48 files changed, 15061 insertions, 0 deletions
diff --git a/src/VBox/Additions/x11/.scm-settings b/src/VBox/Additions/x11/.scm-settings new file mode 100644 index 00000000..4a69dbc9 --- /dev/null +++ b/src/VBox/Additions/x11/.scm-settings @@ -0,0 +1,37 @@ +# $Id: .scm-settings $ +## @file +# Source code massager settings for the x11 guest addition components. +# + +# +# Copyright (C) 2010-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# + +--filter-out-files /undefined_* + +# x11include +--filter-out-files /x11include/*README +/x11include/*: --external-copyright --no-convert-tabs --strip-no-trailing-lines --no-strip-trailing-blanks --no-fix-flower-box-markers --no-convert-eol --no-force-final-eol --dont-set-svn-eol --dont-set-svn-keywords --no-fix-header-guards +/x11include/*h.in: --treat-as .h + +# vboxvideo +--filter-out-files /vboxvideo/README.testing +/vboxvideo/*.c|/vboxvideo/*.h: --license-mit +/vboxvideo/vboxvideo*.c|/vboxvideo/vboxvideo*.h: --license-based-on-mit --no-convert-tabs --dont-set-svn-keywords +/vboxvideo/edid.c: --license-based-on-mit +/vboxvideo/setmode.c: --license-based-on-mit + +# Installer +/Installer/98vboxadd-xclient: --treat-as .sh +--filter-out-files /Installer/vboxvideo.ids +--filter-out-files /Installer/linux_xorg_suse11.conf +--filter-out-files /Installer/solaris_xorg.conf +--filter-out-files /Installer/solaris_xorg_modeless.conf diff --git a/src/VBox/Additions/x11/Installer/98vboxadd-xclient b/src/VBox/Additions/x11/Installer/98vboxadd-xclient new file mode 100755 index 00000000..dcad1ce8 --- /dev/null +++ b/src/VBox/Additions/x11/Installer/98vboxadd-xclient @@ -0,0 +1,39 @@ +#!/bin/sh +## @file +# Start the Guest Additions X11 Client +# + +# +# Copyright (C) 2007-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# + +# Sanity check: if non-writeable PID-files are present in the user home +# directory VBoxClient will fail to start. +for i in $HOME/.vboxclient-*.pid; do + test -w $i || rm -f $i +done + +if ! test -c /dev/vboxguest 2>/dev/null; then + # Do not start if the kernel module is not present. + # Execute notify-send in the back-ground to avoid racing with sddm, + # as notify-send may wait for sddm to start while it waits for us to exit. + notify-send "VBoxClient: the VirtualBox kernel service is not running. Exiting." & +elif test -z "${SSH_CONNECTION}"; then + # This script can also be triggered by a connection over SSH, which is not + # what we had in mind, so we do not start VBoxClient in that case. We do + # not use "exit" here as this script is "source"d, not executed. + /usr/bin/VBoxClient --clipboard + /usr/bin/VBoxClient --checkhostversion + /usr/bin/VBoxClient --display + /usr/bin/VBoxClient --seamless + /usr/bin/VBoxClient --draganddrop + /usr/bin/VBoxClient --vmsvga-x11 # In case VMSVGA emulation is enabled +fi diff --git a/src/VBox/Additions/x11/Installer/linux_xorg_suse11.conf b/src/VBox/Additions/x11/Installer/linux_xorg_suse11.conf new file mode 100644 index 00000000..c55ea88e --- /dev/null +++ b/src/VBox/Additions/x11/Installer/linux_xorg_suse11.conf @@ -0,0 +1,130 @@ +# Default X11 configuration file for SUSE and openSUSE 11 guests in VirtualBox. +# Based on: +# SaX generated X11 config file +# Version: 8.1 +# + +Section "Files" + FontPath "/usr/share/fonts/misc:unscaled" + FontPath "/usr/share/fonts/local" + FontPath "/usr/share/fonts/75dpi:unscaled" + FontPath "/usr/share/fonts/100dpi:unscaled" + FontPath "/usr/share/fonts/Type1" + FontPath "/usr/share/fonts/URW" + FontPath "/usr/share/fonts/Speedo" + FontPath "/usr/share/fonts/PEX" + FontPath "/usr/share/fonts/cyrillic" + FontPath "/usr/share/fonts/latin2/misc:unscaled" + FontPath "/usr/share/fonts/latin2/75dpi:unscaled" + FontPath "/usr/share/fonts/latin2/100dpi:unscaled" + FontPath "/usr/share/fonts/latin2/Type1" + FontPath "/usr/share/fonts/latin7/75dpi:unscaled" + FontPath "/usr/share/fonts/baekmuk:unscaled" + FontPath "/usr/share/fonts/japanese:unscaled" + FontPath "/usr/share/fonts/kwintv" + FontPath "/usr/share/fonts/truetype" + FontPath "/usr/share/fonts/uni:unscaled" + FontPath "/usr/share/fonts/CID" + FontPath "/usr/share/fonts/ucs/misc:unscaled" + FontPath "/usr/share/fonts/ucs/75dpi:unscaled" + FontPath "/usr/share/fonts/ucs/100dpi:unscaled" + FontPath "/usr/share/fonts/hellas/misc:unscaled" + FontPath "/usr/share/fonts/hellas/75dpi:unscaled" + FontPath "/usr/share/fonts/hellas/100dpi:unscaled" + FontPath "/usr/share/fonts/hellas/Type1" + FontPath "/usr/share/fonts/misc/sgi:unscaled" + FontPath "/usr/share/fonts/xtest" + FontPath "/opt/kde3/share/fonts" + InputDevices "/dev/gpmdata" + InputDevices "/dev/input/mice" +EndSection + +Section "ServerFlags" + Option "AllowMouseOpenFail" "on" + Option "ZapWarning" "on" +EndSection + +Section "Module" + Load "dri" + Load "dbe" + Load "freetype" + Load "extmod" + Load "glx" +EndSection + +Section "InputDevice" + Driver "kbd" + Identifier "Keyboard[0]" + Option "Protocol" "Standard" + Option "XkbLayout" "us" + Option "XkbModel" "microsoftpro" + Option "XkbRules" "xfree86" +EndSection + + +Section "InputDevice" + Driver "mouse" + Identifier "Mouse[1]" + Option "Buttons" "9" + Option "Device" "/dev/input/mice" + Option "Name" "VirtualBox Mouse Buttons" + Option "Protocol" "explorerps/2" + Option "Vendor" "Oracle Corporation" + Option "ZAxisMapping" "4 5" +EndSection + + +Section "InputDevice" + Driver "vboxmouse" + Identifier "Mouse[2]" + Option "Device" "/dev/vboxguest" + Option "Name" "VirtualBox Mouse" + Option "Vendor" "Oracle Corporation" +EndSection + + +Section "Monitor" + Identifier "Monitor[0]" + ModelName "VirtualBox Virtual Output" + VendorName "Oracle Corporation" +EndSection + + +Section "Screen" + SubSection "Display" + Depth 24 + EndSubSection + Device "Device[0]" + Identifier "Screen[0]" + Monitor "Monitor[0]" +EndSection + + +Section "Device" + BoardName "VirtualBox Graphics" + Driver "vboxvideo" + Identifier "Device[0]" + VendorName "Oracle Corporation" +EndSection + + + +Section "ServerLayout" + Identifier "Layout[all]" + InputDevice "Keyboard[0]" "CoreKeyboard" + InputDevice "Mouse[1]" "CorePointer" + InputDevice "Mouse[2]" "SendCoreEvents" + Option "Clone" "off" + Option "Xinerama" "off" + Screen "Screen[0]" +EndSection + + +Section "DRI" + Group "video" + Mode 0660 +EndSection + +Section "Extensions" +EndSection + diff --git a/src/VBox/Additions/x11/Installer/solaris_xorg.conf b/src/VBox/Additions/x11/Installer/solaris_xorg.conf new file mode 100644 index 00000000..dd5d780e --- /dev/null +++ b/src/VBox/Additions/x11/Installer/solaris_xorg.conf @@ -0,0 +1,113 @@ +# Default xorg.conf for Solaris guests. +# +# This file was created by VirtualBox Additions installer as it +# was unable to find any existing configuration file for X. + +Section "ServerLayout" + Identifier "X.org Configured" + Screen 0 "Screen0" 0 0 + InputDevice "Mouse0" "CorePointer" + InputDevice "Keyboard0" "CoreKeyboard" +EndSection + +Section "Files" + RgbPath "/usr/X11/lib/X11/rgb" + FontPath "/usr/X11/lib/X11/fonts/misc/:unscaled" + FontPath "/usr/X11/lib/X11/fonts/100dpi/:unscaled" + FontPath "/usr/X11/lib/X11/fonts/75dpi/:unscaled" + FontPath "/usr/X11/lib/X11/fonts/misc/" + FontPath "/usr/X11/lib/X11/fonts/Type1/" + FontPath "/usr/X11/lib/X11/fonts/100dpi/" + FontPath "/usr/X11/lib/X11/fonts/75dpi/" + FontPath "/usr/X11/lib/X11/fonts/TrueType/" + FontPath "/usr/X11/lib/X11/fonts/Type1/sun/" + FontPath "/usr/X11/lib/X11/fonts/F3bitmaps/" +EndSection + +Section "Module" + Load "IA" + Load "dbe" + Load "extmod" + Load "record" + Load "xtrap" + Load "Glcore" + Load "glx" + Load "dri" + Load "xtsol" + Load "type1" + Load "freetype" +EndSection + +Section "InputDevice" + Identifier "Keyboard0" + Driver "kbd" +EndSection + +Section "InputDevice" + Identifier "Mouse0" + Driver "mouse" + Option "CorePointer" + Option "Device" "/dev/mouse" + Option "Protocol" "auto" + Option "ZAxisMapping" "4 5" +EndSection + +Section "Device" + Identifier "Generic Video Card" + Driver "vesa" + BusID "0:2:0" +EndSection + +Section "Monitor" + Identifier "Monitor0" + VendorName "Monitor Vendor" + ModelName "Monitor Model" + HorizSync 30.0 - 110.0 + VertRefresh 50.0 - 150.0 +EndSection + +Section "Device" + Identifier "Card0" + Driver "vesa" + VendorName "Unknown Vendor" + BoardName "Unknown Board" + BusID "PCI:0:2:0" +EndSection + +Section "Screen" + Identifier "Screen0" + Device "Card0" + Monitor "Monitor0" + DefaultDepth 24 + SubSection "Display" + ViewPort 0 0 + Depth 1 + Modes "1024x768_75.00" "800x600_75.00" "640x480_60.00" + EndSubSection + SubSection "Display" + ViewPort 0 0 + Depth 4 + Modes "1024x768_75.00" "800x600_75.00" "640x480_60.00" + EndSubSection + SubSection "Display" + ViewPort 0 0 + Depth 8 + Modes "1024x768_75.00" "800x600_75.00" "640x480_60.00" + EndSubSection + SubSection "Display" + ViewPort 0 0 + Depth 15 + Modes "1024x768_75.00" "800x600_75.00" "640x480_60.00" + EndSubSection + SubSection "Display" + ViewPort 0 0 + Depth 16 + Modes "1024x768_75.00" "800x600_75.00" "640x480_60.00" + EndSubSection + SubSection "Display" + ViewPort 0 0 + Depth 24 + Modes "1024x768_75.00" "800x600_75.00" "640x480_60.00" + EndSubSection +EndSection + diff --git a/src/VBox/Additions/x11/Installer/solaris_xorg_modeless.conf b/src/VBox/Additions/x11/Installer/solaris_xorg_modeless.conf new file mode 100644 index 00000000..0468ba24 --- /dev/null +++ b/src/VBox/Additions/x11/Installer/solaris_xorg_modeless.conf @@ -0,0 +1,81 @@ +# Default xorg.conf for Solaris guests. +# +# This file was created by VirtualBox Additions installer as it +# was unable to find any existing configuration file for X. + +Section "ServerLayout" + Identifier "X.org Configured" + Screen 0 "Screen0" 0 0 + InputDevice "Mouse0" "CorePointer" + InputDevice "Keyboard0" "CoreKeyboard" +EndSection + +Section "Files" + FontPath "/usr/X11/lib/X11/fonts/misc/:unscaled" + FontPath "/usr/X11/lib/X11/fonts/100dpi/:unscaled" + FontPath "/usr/X11/lib/X11/fonts/75dpi/:unscaled" + FontPath "/usr/X11/lib/X11/fonts/misc/" + FontPath "/usr/X11/lib/X11/fonts/Type1/" + FontPath "/usr/X11/lib/X11/fonts/100dpi/" + FontPath "/usr/X11/lib/X11/fonts/75dpi/" + FontPath "/usr/X11/lib/X11/fonts/TrueType/" + FontPath "/usr/X11/lib/X11/fonts/Type1/sun/" + FontPath "/usr/X11/lib/X11/fonts/F3bitmaps/" +EndSection + +Section "Module" + Load "IA" + Load "dbe" + Load "extmod" + Load "record" + Load "xtrap" + Load "Glcore" + Load "glx" + Load "dri" + Load "xtsol" + Load "type1" + Load "freetype" +EndSection + +Section "InputDevice" + Identifier "Keyboard0" + Driver "kbd" +EndSection + +Section "InputDevice" + Identifier "Mouse0" + Driver "mouse" + Option "CorePointer" + Option "Device" "/dev/mouse" + Option "Protocol" "auto" + Option "ZAxisMapping" "4 5" +EndSection + +Section "Device" + Identifier "Generic Video Card" + Driver "vesa" + BusID "0:2:0" +EndSection + +Section "Monitor" + Identifier "Monitor0" + VendorName "Monitor Vendor" + ModelName "Monitor Model" + HorizSync 30.0 - 110.0 + VertRefresh 50.0 - 150.0 +EndSection + +Section "Device" + Identifier "Card0" + Driver "vesa" + VendorName "Unknown Vendor" + BoardName "Unknown Board" + BusID "PCI:0:2:0" +EndSection + +Section "Screen" + Identifier "Screen0" + Device "Card0" + Monitor "Monitor0" +EndSection + diff --git a/src/VBox/Additions/x11/Installer/vboxclient.desktop b/src/VBox/Additions/x11/Installer/vboxclient.desktop new file mode 100644 index 00000000..b5e4d863 --- /dev/null +++ b/src/VBox/Additions/x11/Installer/vboxclient.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Type=Application +Encoding=UTF-8 +Version=1.0 +Name=vboxclient +Name[C]=vboxclient +Comment[C]=VirtualBox User Session Services +Comment=VirtualBox User Session Services +Comment[it]=Servizi di sessione utente di VirtualBox +Comment[pl]=Usługi sesji użytkownika VirtualBox +Exec=/usr/bin/VBoxClient-all +X-GNOME-Autostart-enabled=true +X-KDE-autostart-after=panel diff --git a/src/VBox/Additions/x11/Installer/vboxvideo.ids b/src/VBox/Additions/x11/Installer/vboxvideo.ids new file mode 100644 index 00000000..8286a95e --- /dev/null +++ b/src/VBox/Additions/x11/Installer/vboxvideo.ids @@ -0,0 +1 @@ +80eebeef diff --git a/src/VBox/Additions/x11/Installer/x11config.pl b/src/VBox/Additions/x11/Installer/x11config.pl new file mode 100755 index 00000000..f3058eb5 --- /dev/null +++ b/src/VBox/Additions/x11/Installer/x11config.pl @@ -0,0 +1,129 @@ +#!/usr/bin/perl -w +# $Id: x11config.pl $ +## @file +# Guest Additions X11 config update script +# + +# +# Copyright (C) 2006-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# + +my $temp="/tmp/xorg.conf"; +my $os_type=`uname -s`; +my @cfg_files = ("/etc/X11/xorg.conf-4", "/etc/X11/xorg.conf", "/etc/X11/.xorg.conf", "/etc/xorg.conf", + "/usr/etc/X11/xorg.conf-4", "/usr/etc/X11/xorg.conf", "/usr/lib/X11/xorg.conf-4", + "/usr/lib/X11/xorg.conf", "/etc/X11/XF86Config-4", "/etc/X11/XF86Config", + "/etc/XF86Config", "/usr/X11R6/etc/X11/XF86Config-4", "/usr/X11R6/etc/X11/XF86Config", + "/usr/X11R6/lib/X11/XF86Config-4", "/usr/X11R6/lib/X11/XF86Config"); +my $CFG; +my $TMP; + +my $config_count = 0; + +foreach $cfg (@cfg_files) +{ + + if (open(CFG, $cfg)) + { + open(TMP, ">$temp") or die "Can't create $TMP: $!\n"; + + my $have_mouse = 0; + my $in_section = 0; + + while (defined ($line = <CFG>)) + { + if ($line =~ /^\s*Section\s*"([a-zA-Z]+)"/i) + { + my $section = lc($1); + if (($section eq "inputdevice") || ($section eq "device")) + { + $in_section = 1; + } + if ($section eq "serverlayout") + { + $in_layout = 1; + } + } else { + if ($line =~ /^\s*EndSection/i) + { + $in_section = 0; + $in_layout = 0; + } + } + + if ($in_section) + { + if ($line =~ /^\s*driver\s+\"(?:mouse|vboxmouse)\"/i) + { + $line = " Driver \"vboxmouse\"\n Option \"CorePointer\"\n"; + $have_mouse = 1 + } + + # Other drivers sending events interfere badly with pointer integration + if ($line =~ /^\s*option\s+\"(?:alwayscore|sendcoreevents|corepointer)\"/i) + { + $line = ""; + } + + # Solaris specific: /dev/kdmouse for PS/2 and not /dev/mouse + if ($os_type =~ 'SunOS') + { + if ($line =~ /^\s*option\s+\"(?:device)\"\s+\"(?:\/dev\/mouse)\"/i) + { + $line = " Option \"Device\" \"\/dev\/kdmouse\"\n" + } + } + + if ($line =~ /^\s*driver\s+\"(?:fbdev|vga|vesa|vboxvideo|ChangeMe)\"/i) + { + $line = " Driver \"vboxvideo\"\n"; + } + } + if ($in_layout) + { + # Other drivers sending events interfere badly with pointer integration + if ( $line =~ /^\s*inputdevice.*\"(?:alwayscore|sendcoreevents)\"/i) + { + $line = ""; + } + } + print TMP $line; + } + + if (!$have_mouse) { + print TMP "\n"; + print TMP "Section \"InputDevice\"\n"; + print TMP " Identifier \"VBoxMouse\"\n"; + print TMP " Driver \"vboxmouse\"\n"; + if ($os_type eq 'SunOS') + { + print TMP " Option \"Device\" \"\/dev\/kdmouse\"\n"; + } + print TMP " Option \"CorePointer\"\n"; + print TMP "EndSection\n"; + } + close(TMP); + + rename $cfg, $cfg.".bak"; + system("cp $temp $cfg"); + unlink $temp; + + # Solaris specific: Rename our modified .xorg.conf to xorg.conf for it to be used + if (($os_type =~ 'SunOS') && ($cfg =~ '/etc/X11/.xorg.conf')) + { + system("mv -f $cfg /etc/X11/xorg.conf"); + } + + $config_count++; + } +} + +$config_count != 0 or die "Could not find any X11 configuration files"; diff --git a/src/VBox/Additions/x11/Installer/x11config.sh b/src/VBox/Additions/x11/Installer/x11config.sh new file mode 100755 index 00000000..1890d79d --- /dev/null +++ b/src/VBox/Additions/x11/Installer/x11config.sh @@ -0,0 +1,161 @@ +#!/bin/sh +# $Id: x11config.sh $ +## @file +# Guest Additions X11 config update script +# + +# +# Copyright (C) 2006-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# + +auto_mouse="" +auto_keyboard="" +no_bak="" +old_mouse_dev="/dev/psaux" +video_driver="vboxvideo" + +tab=`printf '\t'` + +ALL_SECTIONS=\ +'^[ '$tab']*[Ss][Ee][Cc][Tt][Ii][Oo][Nn][ '$tab']*'\ +'"\([Ii][Nn][Pp][Uu][Tt][Dd][Ee][Vv][Ii][Cc][Ee]\|'\ +'[Dd][Ee][Vv][Ii][Cc][Ee]\|'\ +'[Ss][Ee][Rr][Vv][Ee][Rr][Ll][Aa][Yy][Oo][Uu][Tt]\|'\ +'[Ss][Cc][Rr][Ee][Ee][Nn]\|'\ +'[Mm][Oo][Nn][Ii][Tt][Oo][Rr]\|'\ +'[Kk][Ee][Yy][Bb][Oo][Aa][Rr][Dd]\|'\ +'[Pp][Oo][Ii][Nn][Tt][Ee][Rr]\)"' +# ^\s*Section\s*"(InputDevice|Device|ServerLayout|Screen|Monitor|Keyboard|Pointer)" + +KBD_SECTION='^[ '$tab']*[Ss][Ee][Cc][Tt][Ii][Oo][Nn][ '$tab']*"'\ +'[Ii][Nn][Pp][Uu][Tt][Dd][Ee][Vv][Ii][Cc][Ee]"' # ^\s*section\s*\"inputdevice\" + +END_SECTION='[Ee][Nn][Dd][Ss][Ee][Cc][Tt][Ii][Oo][Nn]' # EndSection + +OPT_XKB='^[ '$tab']*option[ '$tab'][ '$tab']*"xkb' + +DRIVER_KBD='^[ '$tab']*[Dd][Rr][Ii][Vv][Ee][Rr][ '$tab'][ '$tab']*'\ +'"\(kbd\|keyboard\)"' +# ^\s*driver\s+\"(kbd|keyboard)\" + +reconfigure() +{ + cfg="$1" + tmp="$cfg.vbox.tmp" + test -w "$cfg" || { echo "$cfg does not exist"; return; } + rm -f "$tmp" + test ! -e "$tmp" || { echo "Failed to delete $tmp"; return; } + touch "$tmp" + test -w "$tmp" || { echo "Failed to create $tmp"; return; } + xkb_opts="`cat "$cfg" | sed -n -e "/$KBD_SECTION/,/$END_SECTION/p" | + grep -i "$OPT_XKB"`" + kbd_drv="`cat "$cfg" | sed -n -e "/$KBD_SECTION/,/$END_SECTION/p" | + sed -n -e "0,/$DRIVER_KBD/s/$DRIVER_KBD/\\1/p"`" + test -z "${kbd_drv}" && test -z "${auto_keyboard}" && kbd_drv=keyboard + cat > "$tmp" << EOF +# VirtualBox generated configuration file +# based on $cfg. +EOF + cat "$cfg" | sed -e "/$ALL_SECTIONS/,/$END_SECTION/s/\\(.*\\)/# \\1/" >> "$tmp" + test -n "$kbd_drv" && cat >> "$tmp" << EOF +Section "InputDevice" + Identifier "Keyboard[0]" + Driver "$kbd_drv" +$xkb_opts + Option "Protocol" "Standard" + Option "CoreKeyboard" +EndSection +EOF + kbd_line="" + test -n "$kbd_drv" && kbd_line=' InputDevice "Keyboard[0]" "CoreKeyboard"' + test -z "$auto_mouse" && + cat >> "$tmp" << EOF + +Section "InputDevice" + Driver "mouse" + Identifier "Mouse[1]" + Option "Buttons" "9" + Option "Device" "$old_mouse_dev" + Option "Name" "VirtualBox Mouse Buttons" + Option "Protocol" "explorerps/2" + Option "Vendor" "Oracle Corporation" + Option "ZAxisMapping" "4 5" + Option "CorePointer" +EndSection + +Section "InputDevice" + Driver "vboxmouse" + Identifier "Mouse[2]" + Option "Device" "/dev/vboxguest" + Option "Name" "VirtualBox Mouse" + Option "Vendor" "Oracle Corporation" + Option "SendCoreEvents" +EndSection + +Section "ServerLayout" + Identifier "Layout[all]" +${kbd_line} + InputDevice "Mouse[1]" "CorePointer" + InputDevice "Mouse[2]" "SendCoreEvents" + Option "Clone" "off" + Option "Xinerama" "off" + Screen "Screen[0]" +EndSection +EOF + + cat >> "$tmp" << EOF + +Section "Monitor" + Identifier "Monitor[0]" + ModelName "VirtualBox Virtual Output" + VendorName "Oracle Corporation" +EndSection + +Section "Device" + BoardName "VirtualBox Graphics" + Driver "${video_driver}" + Identifier "Device[0]" + VendorName "Oracle Corporation" +EndSection + +Section "Screen" + SubSection "Display" + Depth 24 + EndSubSection + Device "Device[0]" + Identifier "Screen[0]" + Monitor "Monitor[0]" +EndSection +EOF + + test -n "$no_bak" -o -f "$cfg.vbox" || cp "$cfg" "$cfg.vbox" + test -n "$no_bak" || mv "$cfg" "$cfg.bak" + mv "$tmp" "$cfg" +} + +while test -n "$1" +do + case "$1" in + --autoMouse) + auto_mouse=1 ;; + --autoKeyboard) + auto_keyboard=1 ;; + --noBak) + no_bak=1 ;; + --nopsaux) + old_mouse_dev="/dev/input/mice" ;; + --vmsvga) + video_driver="vmware" ;; + *) + reconfigure "$1" ;; + esac + shift +done diff --git a/src/VBox/Additions/x11/Installer/x11config15.pl b/src/VBox/Additions/x11/Installer/x11config15.pl new file mode 100755 index 00000000..92dd8dde --- /dev/null +++ b/src/VBox/Additions/x11/Installer/x11config15.pl @@ -0,0 +1,87 @@ +#!/usr/bin/perl -w +# $Id: x11config15.pl $ +## @file +# Guest Additions X11 config update script for X.org 1.5 +# + +# +# Copyright (C) 2006-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# + +# What this script does: X.org 1.5 introduces full hardware autodetection +# and no longer requires the user to provide an X.org configuration file. +# However, if such a file is provided, it will override autodetection of +# the graphics card (not of vboxmouse as far as I can see). Although this +# would normally be the user's business, at least Fedora 9 still generates +# a configuration file by default, so we have to rewrite it if we want +# the additions to work on a default guest installation. So we simply go +# through any configuration files we may find on the system and replace +# references to VESA or framebuffer drivers (which might be autodetected +# for use on a VirtualBox guest) and replace them with vboxvideo. + +use File::Copy; + +my $temp="/tmp/xorg.conf"; +# The list of possible names of X.org configuration files +my @cfg_files = ("/etc/X11/xorg.conf-4", "/etc/X11/xorg.conf", "/etc/X11/.xorg.conf", "/etc/xorg.conf", + "/usr/etc/X11/xorg.conf-4", "/usr/etc/X11/xorg.conf", "/usr/lib/X11/xorg.conf-4", + "/usr/lib/X11/xorg.conf"); +my $CFG; +my $TMP; + +# Subroutine to roll back after a partial installation +sub do_fail { + foreach $cfg (@cfg_files) { + move $cfg.".vbox", $cfg; + unlink $cfg.".vbox"; + } + die $1; +} + +# Perform the substitution on any configuration file we may find. +foreach $cfg (@cfg_files) { + + if (open(CFG, $cfg)) { + open(TMP, ">$temp") + or &do_fail("Can't create $TMP: $!\n"); + + while (defined ($line = <CFG>)) { + if ($line =~ /^\s*Section\s*"([a-zA-Z]+)"/i) { + my $section = lc($1); + if ($section eq "device") { + $in_section = 1; + } + } else { + if ($line =~ /^\s*EndSection/i) { + $in_section = 0; + } + } + + if ($in_section) { + if ($line =~ /^\s*driver\s+\"(fbdev|vga|vesa|vboxvideo|ChangeMe)\"/i) { + $line =~ s/(fbdev|vga|vesa|vboxvideo|ChangeMe)/vboxvideo/i; + } + } + print TMP $line; + } + close(TMP); + + # We do not overwrite existing $cfg.".vbox" files because that will + # likely ruin any future attempts to uninstall the additions + copy $cfg, $cfg.".bak"; + if (! -e $cfg.".vbox") { + rename $cfg, $cfg.".vbox"; + } + copy $temp, $cfg + or &do_fail("Could not overwrite configuration file $cfg! Exiting..."); + unlink $temp; + } +} diff --git a/src/VBox/Additions/x11/Installer/x11config15sol.pl b/src/VBox/Additions/x11/Installer/x11config15sol.pl new file mode 100755 index 00000000..a987338d --- /dev/null +++ b/src/VBox/Additions/x11/Installer/x11config15sol.pl @@ -0,0 +1,100 @@ +#!/usr/bin/perl +# $Id: x11config15sol.pl $ +## @file +# Guest Additions X11 config update script +# + +# +# Copyright (C) 2006-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# + +use strict; +use warnings; + +my $temp="/tmp/xorg.conf"; +my $os_type=`uname -s`; +my @cfg_files = ("/etc/X11/xorg.conf", "/etc/X11/.xorg.conf", "/etc/X11/xorg.conf-4", "/etc/xorg.conf", + "/usr/etc/X11/xorg.conf-4", "/usr/etc/X11/xorg.conf", "/usr/lib/X11/xorg.conf-4", + "/usr/lib/X11/xorg.conf", "/etc/X11/XF86Config-4", "/etc/X11/XF86Config", + "/etc/XF86Config", "/usr/X11R6/etc/X11/XF86Config-4", "/usr/X11R6/etc/X11/XF86Config", + "/usr/X11R6/lib/X11/XF86Config-4", "/usr/X11R6/lib/X11/XF86Config"); + +## @todo: r=ramshankar: Hmm, why do we use the same variable name with upper/lower case for different variables? +my $cfg; +my $CFG; +my $TMP; +my $line; +my $config_count = 0; + +# Command line options +if ($#ARGV < 0) +{ + die "x11config15sol.pl: Missing driver name argument to configure for X.org"; +} +my $driver_name = $ARGV[0]; + +# Loop through all possible config files and change them. It's done this wasy for hysterical raisins +# as we didn't know what the correct config file is so we update all of them. However, for Solaris it's +# most likely -only- one of the 2 config files (/etc/X11/xorg.conf, /etc/X11/.xorg.conf). +foreach $cfg (@cfg_files) +{ + if (open(CFG, $cfg)) + { + open(TMP, ">$temp") or die "Can't create $TMP: $!\n"; + + my $in_section = 0; + + while (defined ($line = <CFG>)) + { + if ($line =~ /^\s*Section\s*"([a-zA-Z]+)"/i) + { + my $section = lc($1); + if ($section eq "device") + { + $in_section = 1; + } + } + else + { + if ($line =~ /^\s*EndSection/i) + { + $in_section = 0; + } + } + + if ($in_section) + { + if ($line =~ /^\s*driver\s+\"(?:fbdev|vga|vesa|vboxvideo|ChangeMe)\"/i) + { + $line = " Driver \"$driver_name\"\n"; + } + } + print TMP $line; + } + + close(TMP); + + rename $cfg, $cfg.".bak"; + system("cp $temp $cfg"); + unlink $temp; + + # Solaris specific: Rename our modified .xorg.conf to xorg.conf for it to be used + if (($os_type =~ 'SunOS') && ($cfg =~ '/etc/X11/.xorg.conf')) + { + system("mv -f $cfg /etc/X11/xorg.conf"); + } + + $config_count++; + } +} + +$config_count != 0 or die "Could not find any X11 configuration files"; + diff --git a/src/VBox/Additions/x11/Installer/x11config15suse.pl b/src/VBox/Additions/x11/Installer/x11config15suse.pl new file mode 100755 index 00000000..c9f3ceaa --- /dev/null +++ b/src/VBox/Additions/x11/Installer/x11config15suse.pl @@ -0,0 +1,156 @@ +#!/usr/bin/perl -w +# $Id: x11config15suse.pl $ +## @file +# Guest Additions X11 config update script +# + +# +# Copyright (C) 2006-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# + +# Versions of (open)SUSE which ship X.Org Server 1.5 still do not enable +# mouse autodetection, so on these systems we have to enable vboxmouse in the +# X.Org configuration file as well as vboxvideo. When uninstalling, we enable +# the fbdev driver, which SUSE prefers over vesa, and we leave the references +# to vboxmouse in place, as without the driver they are harmless. + +use File::Copy; + +# This is the file name for the temporary file we write the new configuration +# to. +# @todo: perl must have an API for generating this +my $temp="/tmp/xorg.conf"; +# The list of possible names of X.org configuration files +my @cfg_files = ("/etc/X11/xorg.conf-4", "/etc/X11/xorg.conf", "/etc/X11/.xorg.conf", "/etc/xorg.conf", + "/usr/etc/X11/xorg.conf-4", "/usr/etc/X11/xorg.conf", "/usr/lib/X11/xorg.conf-4", + "/usr/lib/X11/xorg.conf"); +# File descriptor of the old configuration file +my $CFG; +# File descriptor of the temporary file +my $TMP; + +# The name of the mouse driver we are enabling +my $mousedrv = 'vboxmouse'; +# The name of the video driver we are enabling +my $videodrv= 'vboxvideo'; + +# If we are uninstalling, restore the old video driver +if (@ARGV && "$ARGV[0]" eq 'uninstall') +{ + $videodrv = 'fbdev' # SUSE prefers this one +} + +# How many different configuration files have we found? +my $config_count = 0; + +# Subroutine to roll back after a partial installation +sub do_fail { + foreach $cfg (@cfg_files) { + move "$cfg.vbox", $cfg; + unlink "$cfg.vbox"; + } + die $_[0]; +} + +# Perform the substitution on any configuration file we may find. +foreach $cfg (@cfg_files) { + + if (open(CFG, $cfg)) { + open(TMP, ">$temp") + or &do_fail("Can't create $TMP: $!\n"); + + my $have_mouse = 0; + my $in_section = 0; + my $in_layout = 0; + + # Go through the configuration file line by line + while (defined ($line = <CFG>)) { + # Look for the start of sections + if ($line =~ /^\s*Section\s*"([a-zA-Z]+)"/i) { + my $section = lc($1); + # And see if they are device or input device sections + if (($section eq "inputdevice") || $section eq "device") { + $in_section = 1; + } + # Or server layout sections + if ($section eq "serverlayout") + { + $in_section = 1; + $in_layout = 1; + } + } else { + if ($line =~ /^\s*EndSection/i && $in_layout) { + # We always add this to the end of the server layout. + print TMP " InputDevice \"VBoxMouse\"\n" + } + if ($line =~ /^\s*EndSection/i) { + $in_section = 0; + $in_layout = 0; + } + } + + if ($in_section) { + # Inside sections, look for any graphics drivers and replace + # them with our one. + if ($line =~ /^\s*driver\s+\"(fbdev|vga|vesa|vboxvideo|ChangeMe)\"/i) { + $line =~ s/(fbdev|vga|vesa|vboxvideo|ChangeMe)/$videodrv/i; + } + # Also keep track of whether this configuration file contains + # an input device section for vboxmouse. If it does, we don't + # need to add one later. + if ($line =~ /^\s*driver\s+\"(?:vboxmouse)\"/i) + { + $have_mouse = 1 + } + + # We add vboxmouse to the server layout section ourselves, so + # remove any existing references to it. + if ( $line =~ /^\s*inputdevice.*\"vboxmouse\"/i) + { + $line = ""; + } + } + print TMP $line; + } + + # We always add a vboxmouse section at the end for SUSE guests using + # X.Org 1.5 if vboxmouse is not referenced anywhere else in the file, + # and we do not remove it when we uninstall the additions, as it will + # not do any harm if it is left. + if (!$have_mouse) { + print TMP "\n"; + print TMP "Section \"InputDevice\"\n"; + print TMP " Identifier \"VBoxMouse\"\n"; + print TMP " Driver \"$mousedrv\"\n"; + print TMP " Option \"Device\" \"\/dev\/vboxguest\"\n"; + print TMP " Option \"SendCoreEvents\" \"on\"\n"; + print TMP "EndSection\n"; + } + close(TMP); + + # We do not overwrite existing "$cfg.vbox" files in order to keep a + # record of what the configuration looked like before the very first + # installation of the additions. + copy $cfg, "$cfg.bak"; + if (! -e "$cfg.vbox") { + rename $cfg, "$cfg.vbox"; + } + copy $temp, $cfg + or &do_fail("Could not overwrite configuration file $cfg! Exiting..."); + unlink $temp; + + $config_count++; + } +} + +# Warn if we did not find any configuration files +$config_count != 0 or die "Could not find any X11 configuration files"; + diff --git a/src/VBox/Additions/x11/Installer/x11restore.pl b/src/VBox/Additions/x11/Installer/x11restore.pl new file mode 100755 index 00000000..0131c6ae --- /dev/null +++ b/src/VBox/Additions/x11/Installer/x11restore.pl @@ -0,0 +1,67 @@ +#!/usr/bin/perl -w +# $Id: x11restore.pl $ +## @file +# Restore xorg.conf while removing Guest Additions. +# + +# +# Copyright (C) 2008-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# + + +my $os_type=`uname -s`; +my @cfg_files = ("/etc/X11/xorg.conf-4", "/etc/X11/xorg.conf", "/etc/X11/.xorg.conf", "/etc/xorg.conf", + "/usr/etc/X11/xorg.conf-4", "/usr/etc/X11/xorg.conf", "/usr/lib/X11/xorg.conf-4", + "/usr/lib/X11/xorg.conf", "/etc/X11/XF86Config-4", "/etc/X11/XF86Config", + "/etc/XF86Config", "/usr/X11R6/etc/X11/XF86Config-4", "/usr/X11R6/etc/X11/XF86Config", + "/usr/X11R6/lib/X11/XF86Config-4", "/usr/X11R6/lib/X11/XF86Config"); +my $CFG; +my $BAK; + +my $config_count = 0; +my $vboxpresent = "vboxvideo"; + +foreach $cfg (@cfg_files) +{ + if (open(CFG, $cfg)) + { + @array=<CFG>; + close(CFG); + + foreach $line (@array) + { + if ($line =~ /$vboxpresent/) + { + if (open(BAK, $cfg.".bak")) + { + close(BAK); + rename $cfg.".bak", $cfg; + } + else + { + # On Solaris just delete existing conf if backup is not found (Possible on distros like Indiana) + if ($os_type =~ 'SunOS') + { + unlink $cfg + } + else + { + die "Failed to restore xorg.conf! Your existing config. still uses VirtualBox drivers!!"; + } + } + } + } + $config_count++; + } +} + +$config_count != 0 or die "Could not find backed-up xorg.conf to restore it."; + diff --git a/src/VBox/Additions/x11/Makefile.kmk b/src/VBox/Additions/x11/Makefile.kmk new file mode 100644 index 00000000..b71dbf41 --- /dev/null +++ b/src/VBox/Additions/x11/Makefile.kmk @@ -0,0 +1,40 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the X11 Guest Additions. +# + +# +# Copyright (C) 2006-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + +# Include sub-makefiles. +if1of ($(KBUILD_TARGET), freebsd linux netbsd openbsd solaris) + include $(PATH_SUB_CURRENT)/VBoxClient/Makefile.kmk + ifndef VBOX_NO_LEGACY_XORG_X11 + include $(PATH_SUB_CURRENT)/vboxvideo/Makefile.kmk + ifn1of ($(KBUILD_TARGET), netbsd solaris) + include $(PATH_SUB_CURRENT)/vboxmouse/Makefile.kmk + endif + # This should logically only be controlled by VBOX_NO_LEGACY_XORG_X11, + # as it is not used for drivers at all, but rather to build X11 clients + # on systems missing needed libraries. + ## @todo fix at some later point when it will not break people's workflows. + ifndef VBOX_USE_SYSTEM_XORG_HEADERS + include $(PATH_SUB_CURRENT)/x11stubs/Makefile.kmk + endif + endif +endif + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/Additions/x11/VBoxClient/Makefile.kmk b/src/VBox/Additions/x11/VBoxClient/Makefile.kmk new file mode 100644 index 00000000..d5de3c88 --- /dev/null +++ b/src/VBox/Additions/x11/VBoxClient/Makefile.kmk @@ -0,0 +1,141 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the VirtualBox Guest Addition X11 Client. +# + +# +# Copyright (C) 2006-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# + +SUB_DEPTH = ../../../../.. +include $(KBUILD_PATH)/subheader.kmk + +# +# VBoxClient - clipboard and seamless. +# +PROGRAMS += VBoxClient + +VBoxClient_TEMPLATE = NewVBoxGuestR3Exe +VBoxClient_DEFS += VBOX_X11_CLIPBOARD VBOX_WITH_HGCM +ifdef VBOX_WITH_DBUS + VBoxClient_DEFS += VBOX_WITH_DBUS +endif +VBoxClient_DEFS.linux += _GNU_SOURCE +VBoxClient_INCS = $(VBOX_GRAPHICS_INCS) +VBoxClient_SOURCES = \ + main.cpp \ + $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-helper.cpp \ + $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/x11-clipboard.cpp \ + clipboard.cpp \ + display-svga.cpp \ + display-svga-x11.cpp \ + seamless.cpp \ + seamless-x11.cpp \ + display.cpp \ + hostversion.cpp \ + check3d.cpp +VBoxClient_SOURCES.linux = \ + chk_stubs.c +VBoxClient_LIBPATH = \ + $(VBOX_LIBPATH32_X11) +VBoxClient_LIBS.freebsd = \ + iconv +VBoxClient_LIBS.linux = \ + dl +VBoxClient_LIBS.solaris = \ + dl +VBoxClient_LIBS = \ + X11 Xrandr Xt Xext Xmu + +# XXX: -L comes from the template, but not rpath +VBoxClient_LDFLAGS.netbsd = \ + -Wl,-rpath /usr/X11R7/lib + +ifdef VBOX_WITH_DRAG_AND_DROP + ifdef VBOX_DND_WITH_XTEST + VBoxClient_DEFS += VBOX_DND_WITH_XTEST + VBoxClient_LIBS += \ + Xtst + endif +endif + +# These are static replacements for gcc-specific parts of libstdc++ +VBoxClient_LIBS += \ + supc++ \ + gcc_eh + +# This forces the memcpy references in the static libraries to go to +# __wrap_memcpy, which we can wrap around memcpy@GLIBC_2.2.5. I do not know +# how else to do that without recompiling or implementing our own memcpy. +ifeq ($(KBUILD_TARGET),linux) +VBoxClient_LDFLAGS.amd64 += \ + -Wl,--wrap=memcpy +endif + +ifdef VBOX_WITH_GUEST_PROPS +VBoxClient_DEFS += VBOX_WITH_GUEST_PROPS +endif +ifdef VBOX_WITH_DRAG_AND_DROP +VBoxClient_DEFS += \ + VBOX_WITH_DRAG_AND_DROP \ + $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,) +VBoxClient_SOURCES += \ + draganddrop.cpp +VBoxClient_LIBS += \ + $(VBOX_LIB_VBGL_R3) \ + $(PATH_STAGE_LIB)/additions/VBoxDnDGuestR3Lib$(VBOX_SUFF_LIB) +endif + +if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK) + if1of ($(KBUILD_TARGET), freebsd linux netbsd openbsd solaris) + +# Set this in LocalConfig.kmk if you are working on the X11 clipboard service +# to automatically run the unit test at build time. +# OTHERS += $(tstSeamlessX11-auto_0_OUTDIR)/tstSeamlessX11-auto.run + + PROGRAMS += tstSeamlessX11-auto + tstSeamlessX11-auto_TEMPLATE = VBOXR3TSTEXE + tstSeamlessX11-auto_SOURCES = \ + testcase/tstSeamlessX11-auto.cpp \ + seamless-x11.cpp + tstSeamlessX11-auto_DEFS = TESTCASE + tstSeamlessX11-auto_LIBS = \ + $(LIB_RUNTIME) + + TESTING += $(tstSeamlessX11-auto_0_OUTDIR)/tstSeamlessX11-auto +$$(tstSeamlessX11-auto_0_OUTDIR)/tstSeamlessX11-auto.run: \ + $$(tstSeamlessX11-auto_1_STAGE_TARGET) + export VBOX_LOG_DEST=nofile; $(tstSeamlessX11-auto_1_STAGE_TARGET) quiet + $(QUIET)$(APPEND) -t "$@" "done" + + # + # Additional testcase designed to be run manually, which initiates and starts the Linux + # guest client part of the seamless additions in the host, faking seamless events from + # the host and writing information about guest events to standard output. + # + PROGRAMS += tstSeamlessX11 + tstSeamlessX11_TEMPLATE = VBOXR3TSTEXE + tstSeamlessX11_SOURCES = \ + testcase/tstSeamlessX11.cpp \ + seamless.cpp \ + seamless-x11.cpp + tstSeamlessX11_LIBPATH = \ + $(VBOX_LIBPATH_X11) + tstSeamlessX11_LIBS = \ + $(LIB_RUNTIME) \ + Xext \ + Xmu \ + X11 + endif +endif + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/Additions/x11/VBoxClient/VBoxClient.h b/src/VBox/Additions/x11/VBoxClient/VBoxClient.h new file mode 100644 index 00000000..4feb09de --- /dev/null +++ b/src/VBox/Additions/x11/VBoxClient/VBoxClient.h @@ -0,0 +1,88 @@ +/* $Id: VBoxClient.h $ */ +/** @file + * + * VirtualBox additions user session daemon. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#ifndef GA_INCLUDED_SRC_x11_VBoxClient_VBoxClient_h +#define GA_INCLUDED_SRC_x11_VBoxClient_VBoxClient_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <VBox/log.h> +#include <iprt/cpp/utils.h> +#include <iprt/string.h> + +/** Exit with a fatal error. */ +#define VBClFatalError(format) \ +do { \ + char *pszMessage = RTStrAPrintf2 format; \ + LogRel(format); \ + vbclFatalError(pszMessage); \ +} while(0) + +/** Exit with a fatal error. */ +extern DECLNORETURN(void) vbclFatalError(char *pszMessage); + +/** Call clean-up for the current service and exit. */ +extern void VBClCleanUp(bool fExit = true); + +/** A simple interface describing a service. VBoxClient will run exactly one + * service per invocation. */ +struct VBCLSERVICE +{ + /** Get the services default path to pidfile, relative to $HOME */ + /** @todo Should this also have a component relative to the X server number? + */ + const char *(*getPidFilePath)(void); + /** Special initialisation, if needed. @a pause and @a resume are + * guaranteed not to be called until after this returns. */ + int (*init)(struct VBCLSERVICE **ppInterface); + /** Run the service main loop */ + int (*run)(struct VBCLSERVICE **ppInterface, bool fDaemonised); + /** Clean up any global resources before we shut down hard. The last calls + * to @a pause and @a resume are guaranteed to finish before this is called. + */ + void (*cleanup)(struct VBCLSERVICE **ppInterface); +}; + +/** Default handler for various struct VBCLSERVICE member functions. */ +DECLINLINE(int) VBClServiceDefaultHandler(struct VBCLSERVICE **pSelf) +{ + RT_NOREF1(pSelf); + return VINF_SUCCESS; +} + +/** Default handler for the struct VBCLSERVICE clean-up member function. + * Usually used because the service is cleaned up automatically when the user + * process/X11 exits. */ +DECLINLINE(void) VBClServiceDefaultCleanup(struct VBCLSERVICE **ppInterface) +{ + NOREF(ppInterface); +} + +extern struct VBCLSERVICE **VBClGetClipboardService(); +extern struct VBCLSERVICE **VBClGetSeamlessService(); +extern struct VBCLSERVICE **VBClGetDisplayService(); +extern struct VBCLSERVICE **VBClGetHostVersionService(); +#ifdef VBOX_WITH_DRAG_AND_DROP +extern struct VBCLSERVICE **VBClGetDragAndDropService(); +#endif /* VBOX_WITH_DRAG_AND_DROP */ +extern struct VBCLSERVICE **VBClCheck3DService(); +extern struct VBCLSERVICE **VBClDisplaySVGAService(); +extern struct VBCLSERVICE **VBClDisplaySVGAX11Service(); + +#endif /* !GA_INCLUDED_SRC_x11_VBoxClient_VBoxClient_h */ diff --git a/src/VBox/Additions/x11/VBoxClient/check3d.cpp b/src/VBox/Additions/x11/VBoxClient/check3d.cpp new file mode 100644 index 00000000..d091f369 --- /dev/null +++ b/src/VBox/Additions/x11/VBoxClient/check3d.cpp @@ -0,0 +1,73 @@ +/* $Id: check3d.cpp $ */ +/** @file + * X11 guest client - 3D pass-through check. + */ + +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include "VBoxClient.h" + +#include <VBox/VBoxGuest.h> +#include <VBox/VBoxGuestLib.h> +#include <VBox/HostServices/VBoxCrOpenGLSvc.h> + +#include <stdlib.h> + +#define CR_VBOX_CAP_HOST_CAPS_NOT_SUFFICIENT 0x00000020 + +static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised) +{ + int rc; + HGCMCLIENTID idClient; + CRVBOXHGCMGETCAPS caps; + LogFlowFunc(("\n")); + + NOREF(ppInterface); + NOREF(fDaemonised); + /* Initialise the guest library. */ + rc = VbglR3InitUser(); + if (RT_FAILURE(rc)) + exit(1); + rc = VbglR3HGCMConnect("VBoxSharedCrOpenGL", &idClient); + if (RT_FAILURE(rc)) + exit(1); + + VBGL_HGCM_HDR_INIT(&caps.hdr, idClient, SHCRGL_GUEST_FN_GET_CAPS_LEGACY, SHCRGL_CPARMS_GET_CAPS_LEGACY); + caps.Caps.type = VMMDevHGCMParmType_32bit; + caps.Caps.u.value32 = 0; + rc = VbglR3HGCMCall(&caps.hdr, sizeof(caps)); + if (RT_FAILURE(rc)) + exit(1); + if (caps.Caps.u.value32 & CR_VBOX_CAP_HOST_CAPS_NOT_SUFFICIENT) + exit(1); + VbglR3HGCMDisconnect(idClient); + VbglR3Term(); + LogFlowFunc(("returning %Rrc\n", rc)); + return rc; +} + +static struct VBCLSERVICE g_vbclCheck3DInterface = +{ + NULL, + VBClServiceDefaultHandler, /* init */ + run, + VBClServiceDefaultCleanup +}; +static struct VBCLSERVICE *g_pvbclCheck3DInterface = &g_vbclCheck3DInterface; + +/* Static factory */ +struct VBCLSERVICE **VBClCheck3DService() +{ + return &g_pvbclCheck3DInterface; +} + diff --git a/src/VBox/Additions/x11/VBoxClient/chk_stubs.c b/src/VBox/Additions/x11/VBoxClient/chk_stubs.c new file mode 100644 index 00000000..1a794469 --- /dev/null +++ b/src/VBox/Additions/x11/VBoxClient/chk_stubs.c @@ -0,0 +1,59 @@ +/* $Id: chk_stubs.c $ */ +/** @file + * glibc stubs for the VirtualBox Guest Addition X11 Client. + */ + +/* + * Copyright (C) 2018-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/* If we want the binary to be usable with glibc 2.3, we have to prevent + VBoxClient from containing later symbols. This includes resolution of + symbols from supc++ and gcc_eh. */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> + +extern int __sprintf_chk(char *psz, int fFlags, size_t cb, const char *pszFormat, ...); +int __sprintf_chk(char *psz, int fFlags, size_t cb, const char *pszFormat, ...) +{ + int rc; + va_list va; + + (void)fFlags; + va_start(va, pszFormat); + rc = vsnprintf(psz, cb, pszFormat, va); + va_end(va); + return rc; +} + +extern void __stack_chk_fail(void); +void __stack_chk_fail(void) +{ + fprintf(stderr, "Stack check failed!\n"); + _exit(1); +} + +#ifdef __x86_64 +/* Furthermore, wrap references to memcpy to force them to go to the right + * version. We are forced to do it this way because the shared libraries + * supc++ and gcc_eh contain references which we cannot change. */ + +extern void *__wrap_memcpy(void *dest, const void *src, size_t n); + +asm (".symver memcpy, memcpy@GLIBC_2.2.5"); +void *__wrap_memcpy(void *dest, const void *src, size_t n) +{ + return memcpy(dest, src, n); +} +#endif diff --git a/src/VBox/Additions/x11/VBoxClient/clipboard.cpp b/src/VBox/Additions/x11/VBoxClient/clipboard.cpp new file mode 100644 index 00000000..145eba9c --- /dev/null +++ b/src/VBox/Additions/x11/VBoxClient/clipboard.cpp @@ -0,0 +1,335 @@ +/** $Id: clipboard.cpp $ */ +/** @file + * Guest Additions - X11 Shared Clipboard. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/alloc.h> +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/initterm.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/process.h> +#include <iprt/semaphore.h> + +#include <VBox/log.h> +#include <VBox/VBoxGuestLib.h> +#include <VBox/HostServices/VBoxClipboardSvc.h> +#include <VBox/GuestHost/SharedClipboard.h> + +#include "VBoxClient.h" + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ + +/** + * Global clipboard context information. + */ +struct _VBOXCLIPBOARDCONTEXT +{ + /** Client ID for the clipboard subsystem */ + uint32_t client; + + /** Pointer to the X11 clipboard backend */ + CLIPBACKEND *pBackend; +}; + +/** Only one client is supported. There seems to be no need for more clients. */ +static VBOXCLIPBOARDCONTEXT g_ctx; + + +/** + * Transfer clipboard data from the guest to the host. + * + * @returns VBox result code + * @param u32Format The format of the data being sent + * @param pv Pointer to the data being sent + * @param cb Size of the data being sent in bytes + */ +static int vboxClipboardSendData(uint32_t u32Format, void *pv, uint32_t cb) +{ + int rc; + LogRelFlowFunc(("u32Format=%d, pv=%p, cb=%d\n", u32Format, pv, cb)); + rc = VbglR3ClipboardWriteData(g_ctx.client, u32Format, pv, cb); + LogRelFlowFunc(("rc=%Rrc\n", rc)); + return rc; +} + + +/** + * Get clipboard data from the host. + * + * @returns VBox result code + * @param pCtx Our context information + * @param u32Format The format of the data being requested + * @retval ppv On success and if pcb > 0, this will point to a buffer + * to be freed with RTMemFree containing the data read. + * @retval pcb On success, this contains the number of bytes of data + * returned + */ +int ClipRequestDataForX11(VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format, void **ppv, uint32_t *pcb) +{ + RT_NOREF1(pCtx); + int rc = VINF_SUCCESS; + uint32_t cb = 1024; + void *pv = RTMemAlloc(cb); + + *ppv = 0; + LogRelFlowFunc(("u32Format=%u\n", u32Format)); + if (RT_UNLIKELY(!pv)) + rc = VERR_NO_MEMORY; + if (RT_SUCCESS(rc)) + rc = VbglR3ClipboardReadData(g_ctx.client, u32Format, pv, cb, pcb); + if (RT_SUCCESS(rc) && (rc != VINF_BUFFER_OVERFLOW)) + *ppv = pv; + /* A return value of VINF_BUFFER_OVERFLOW tells us to try again with a + * larger buffer. The size of the buffer needed is placed in *pcb. + * So we start all over again. */ + if (rc == VINF_BUFFER_OVERFLOW) + { + cb = *pcb; + RTMemFree(pv); + pv = RTMemAlloc(cb); + if (RT_UNLIKELY(!pv)) + rc = VERR_NO_MEMORY; + if (RT_SUCCESS(rc)) + rc = VbglR3ClipboardReadData(g_ctx.client, u32Format, pv, cb, pcb); + if (RT_SUCCESS(rc) && (rc != VINF_BUFFER_OVERFLOW)) + *ppv = pv; + } + /* Catch other errors. This also catches the case in which the buffer was + * too small a second time, possibly because the clipboard contents + * changed half-way through the operation. Since we can't say whether or + * not this is actually an error, we just return size 0. + */ + if (RT_FAILURE(rc) || (VINF_BUFFER_OVERFLOW == rc)) + { + *pcb = 0; + if (pv != NULL) + RTMemFree(pv); + } + LogRelFlowFunc(("returning %Rrc\n", rc)); + if (RT_SUCCESS(rc)) + LogRelFlow((" *pcb=%d\n", *pcb)); + return rc; +} + +/** Opaque data structure describing a request from the host for clipboard + * data, passed in when the request is forwarded to the X11 backend so that + * it can be completed correctly. */ +struct _CLIPREADCBREQ +{ + /** The data format that was requested. */ + uint32_t u32Format; +}; + +/** + * Tell the host that new clipboard formats are available. + * + * @param pCtx Our context information + * @param u32Formats The formats to advertise + */ +void ClipReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Formats) +{ + RT_NOREF1(pCtx); + LogRelFlowFunc(("u32Formats=%d\n", u32Formats)); + int rc = VbglR3ClipboardReportFormats(g_ctx.client, u32Formats); + LogRelFlowFunc(("rc=%Rrc\n", rc)); +} + +/** This is called by the backend to tell us that a request for data from + * X11 has completed. + * @param pCtx Our context information + * @param rc the iprt result code of the request + * @param pReq the request structure that we passed in when we started + * the request. We RTMemFree() this in this function. + * @param pv the clipboard data returned from X11 if the request + * succeeded (see @a rc) + * @param cb the size of the data in @a pv + */ +void ClipCompleteDataRequestFromX11(VBOXCLIPBOARDCONTEXT *pCtx, int rc, CLIPREADCBREQ *pReq, void *pv, uint32_t cb) +{ + RT_NOREF1(pCtx); + if (RT_SUCCESS(rc)) + vboxClipboardSendData(pReq->u32Format, pv, cb); + else + vboxClipboardSendData(0, NULL, 0); + RTMemFree(pReq); +} + +/** + * Connect the guest clipboard to the host. + * + * @returns VBox status code + */ +int vboxClipboardConnect(void) +{ + int rc = VINF_SUCCESS; + LogRelFlowFunc(("\n")); + + /* Sanity */ + AssertReturn(g_ctx.client == 0, VERR_WRONG_ORDER); + g_ctx.pBackend = ClipConstructX11(&g_ctx, false); + if (!g_ctx.pBackend) + rc = VERR_NO_MEMORY; + if (RT_SUCCESS(rc)) + rc = ClipStartX11(g_ctx.pBackend); + if (RT_SUCCESS(rc)) + { + rc = VbglR3ClipboardConnect(&g_ctx.client); + if (RT_FAILURE(rc)) + LogRel(("Error connecting to host. rc=%Rrc\n", rc)); + else if (!g_ctx.client) + { + LogRel(("Invalid client ID of 0\n")); + rc = VERR_NOT_SUPPORTED; + } + } + + if (rc != VINF_SUCCESS && g_ctx.pBackend) + ClipDestructX11(g_ctx.pBackend); + LogRelFlowFunc(("g_ctx.client=%u rc=%Rrc\n", g_ctx.client, rc)); + return rc; +} + +/** + * The main loop of our clipboard reader. + */ +int vboxClipboardMain(void) +{ + int rc; + LogRelFlowFunc(("Starting guest clipboard service\n")); + bool fExiting = false; + + while (!fExiting) + { + uint32_t Msg; + uint32_t fFormats; + rc = VbglR3ClipboardGetHostMsg(g_ctx.client, &Msg, &fFormats); + if (RT_SUCCESS(rc)) + { + switch (Msg) + { + case VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS: + { + /* The host has announced available clipboard formats. + * Save the information so that it is available for + * future requests from guest applications. + */ + LogRelFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS fFormats=%x\n", fFormats)); + ClipAnnounceFormatToX11(g_ctx.pBackend, fFormats); + break; + } + + case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA: + { + /* The host needs data in the specified format. */ + LogRelFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA fFormats=%x\n", fFormats)); + CLIPREADCBREQ *pReq; + pReq = (CLIPREADCBREQ *)RTMemAllocZ(sizeof(*pReq)); + if (!pReq) + { + rc = VERR_NO_MEMORY; + fExiting = true; + } + else + { + pReq->u32Format = fFormats; + ClipRequestDataFromX11(g_ctx.pBackend, fFormats, + pReq); + } + break; + } + + case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT: + { + /* The host is terminating. */ + LogRelFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT\n")); + if (RT_SUCCESS(ClipStopX11(g_ctx.pBackend))) + ClipDestructX11(g_ctx.pBackend); + fExiting = true; + break; + } + + default: + LogRel2(("Unsupported message from host!!!\n")); + } + } + + LogRelFlow(("processed host event rc = %d\n", rc)); + } + LogRelFlowFunc(("rc=%d\n", rc)); + return rc; +} + +static const char *getPidFilePath() +{ + return ".vboxclient-clipboard.pid"; +} + +static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised) +{ + RT_NOREF2(ppInterface, fDaemonised); + + /* Initialise the guest library. */ + int rc = VbglR3InitUser(); + if (RT_FAILURE(rc)) + VBClFatalError(("Failed to connect to the VirtualBox kernel service, rc=%Rrc\n", rc)); + rc = vboxClipboardConnect(); + /* Not RT_SUCCESS: VINF_PERMISSION_DENIED is host service not present. */ + if (rc == VINF_SUCCESS) + rc = vboxClipboardMain(); + if (rc == VERR_NOT_SUPPORTED) + rc = VINF_SUCCESS; /* Prevent automatic restart. */ + if (RT_FAILURE(rc)) + LogRelFunc(("guest clipboard service terminated abnormally: return code %Rrc\n", rc)); + return rc; +} + +static void cleanup(struct VBCLSERVICE **ppInterface) +{ + NOREF(ppInterface); + VbglR3Term(); +} + +struct VBCLSERVICE vbclClipboardInterface = +{ + getPidFilePath, + VBClServiceDefaultHandler, /* init */ + run, + cleanup +}; + +struct CLIPBOARDSERVICE +{ + struct VBCLSERVICE *pInterface; +}; + +struct VBCLSERVICE **VBClGetClipboardService() +{ + struct CLIPBOARDSERVICE *pService = + (struct CLIPBOARDSERVICE *)RTMemAlloc(sizeof(*pService)); + + if (!pService) + VBClFatalError(("Out of memory\n")); + pService->pInterface = &vbclClipboardInterface; + return &pService->pInterface; +} diff --git a/src/VBox/Additions/x11/VBoxClient/display-svga-x11.cpp b/src/VBox/Additions/x11/VBoxClient/display-svga-x11.cpp new file mode 100644 index 00000000..08b3194e --- /dev/null +++ b/src/VBox/Additions/x11/VBoxClient/display-svga-x11.cpp @@ -0,0 +1,320 @@ +/* $Id: display-svga-x11.cpp $ */ +/** @file + * X11 guest client - VMSVGA emulation resize event pass-through to X.Org + * guest driver. + */ + +/* + * Copyright (C) 2017-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/* + * Known things to test when changing this code. All assume a guest with VMSVGA + * active and controlled by X11 or Wayland, and Guest Additions installed and + * running, unless otherwise stated. + * - On Linux 4.6 and later guests, VBoxClient --vmsvga should be running as + * root and not as the logged-in user. Dynamic resizing should work for all + * screens in any environment which handles kernel resize notifications, + * including at log-in screens. Test GNOME Shell Wayland and GNOME Shell + * under X.Org or Unity or KDE at the log-in screen and after log-in. + * - Linux 4.10 changed the user-kernel-ABI introduced in 4.6: test both. + * - On other guests (than Linux 4.6 or later) running X.Org Server 1.3 or + * later, VBoxClient --vmsvga should never be running as root, and should run + * (and dynamic resizing and screen enable/disable should work for all + * screens) whenever a user is logged in to a supported desktop environment. + * - On guests running X.Org Server 1.2 or older, VBoxClient --vmsvga should + * never run as root and should run whenever a user is logged in to a + * supported desktop environment. Dynamic resizing should work for the first + * screen, and enabling others should not be possible. + * - When VMSVGA is not enabled, VBoxClient --vmsvga should never stay running. + */ + +#include "VBoxClient.h" + +#include <VBox/VBoxGuestLib.h> + +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/file.h> +#include <iprt/string.h> + +#include <sys/utsname.h> + +/** Maximum number of supported screens. DRM and X11 both limit this to 32. */ +/** @todo if this ever changes, dynamically allocate resizeable arrays in the + * context structure. */ +#define VMW_MAX_HEADS 32 + +/* VMWare X.Org driver control parts definitions. */ + +#include <X11/Xlibint.h> + +static bool checkRecentLinuxKernel(void) +{ + struct utsname name; + + if (uname(&name)) + VBClFatalError(("Failed to get kernel name.\n")); + if (strcmp(name.sysname, "Linux")) + return false; + return (RTStrVersionCompare(name.release, "4.6") >= 0); +} + +struct X11CONTEXT +{ + Display *pDisplay; + int hRandRMajor; + int hVMWMajor; +}; + +static void x11Connect(struct X11CONTEXT *pContext) +{ + int dummy; + + if (pContext->pDisplay != NULL) + VBClFatalError(("%s called with bad argument\n", __func__)); + pContext->pDisplay = XOpenDisplay(NULL); + if (pContext->pDisplay == NULL) + return; + if ( !XQueryExtension(pContext->pDisplay, "RANDR", + &pContext->hRandRMajor, &dummy, &dummy) + || !XQueryExtension(pContext->pDisplay, "VMWARE_CTRL", + &pContext->hVMWMajor, &dummy, &dummy)) + { + XCloseDisplay(pContext->pDisplay); + pContext->pDisplay = NULL; + } +} + +#define X11_VMW_TOPOLOGY_REQ 2 +struct X11VMWRECT /* xXineramaScreenInfo in Xlib headers. */ +{ + int16_t x; + int16_t y; + uint16_t w; + uint16_t h; +}; +AssertCompileSize(struct X11VMWRECT, 8); + +struct X11REQHEADER +{ + uint8_t hMajor; + uint8_t idType; + uint16_t cd; +}; + +struct X11VMWTOPOLOGYREQ +{ + struct X11REQHEADER header; + uint32_t idX11Screen; + uint32_t cScreens; + uint32_t u32Pad; + struct X11VMWRECT aRects[1]; +}; +AssertCompileSize(struct X11VMWTOPOLOGYREQ, 24); + +#define X11_VMW_TOPOLOGY_REPLY_SIZE 32 + +#define X11_VMW_RESOLUTION_REQUEST 1 +struct X11VMWRESOLUTIONREQ +{ + struct X11REQHEADER header; + uint32_t idX11Screen; + uint32_t w; + uint32_t h; +}; +AssertCompileSize(struct X11VMWRESOLUTIONREQ, 16); + +#define X11_VMW_RESOLUTION_REPLY_SIZE 32 + +#define X11_RANDR_GET_SCREEN_REQUEST 5 +struct X11RANDRGETSCREENREQ +{ + struct X11REQHEADER header; + uint32_t hWindow; +}; +AssertCompileSize(struct X11RANDRGETSCREENREQ, 8); + +#define X11_RANDR_GET_SCREEN_REPLY_SIZE 32 + +/* This was a macro in old Xlib versions and a function in newer ones; the + * display members touched by the macro were declared as ABI for compatibility + * reasons. To simplify building with different generations, we duplicate the + * code. */ +static void x11GetRequest(struct X11CONTEXT *pContext, uint8_t hMajor, + uint8_t idType, size_t cb, struct X11REQHEADER **ppReq) +{ + if (pContext->pDisplay->bufptr + cb > pContext->pDisplay->bufmax) + _XFlush(pContext->pDisplay); + if (pContext->pDisplay->bufptr + cb > pContext->pDisplay->bufmax) + VBClFatalError(("%s display buffer overflow.\n", __func__)); + if (cb % 4 != 0) + VBClFatalError(("%s bad parameter.\n", __func__)); + pContext->pDisplay->last_req = pContext->pDisplay->bufptr; + *ppReq = (struct X11REQHEADER *)pContext->pDisplay->bufptr; + (*ppReq)->hMajor = hMajor; + (*ppReq)->idType = idType; + (*ppReq)->cd = cb / 4; + pContext->pDisplay->bufptr += cb; + pContext->pDisplay->request++; +} + +static void x11SendHints(struct X11CONTEXT *pContext, struct X11VMWRECT *pRects, + unsigned cRects) +{ + unsigned i; + struct X11VMWTOPOLOGYREQ *pReqTopology; + uint8_t repTopology[X11_VMW_TOPOLOGY_REPLY_SIZE]; + struct X11VMWRESOLUTIONREQ *pReqResolution; + uint8_t repResolution[X11_VMW_RESOLUTION_REPLY_SIZE]; + + if (!VALID_PTR(pContext->pDisplay)) + VBClFatalError(("%s bad display argument.\n", __func__)); + if (cRects == 0) + return; + /* Try a topology (multiple screen) request. */ + x11GetRequest(pContext, pContext->hVMWMajor, X11_VMW_TOPOLOGY_REQ, + sizeof(struct X11VMWTOPOLOGYREQ) + + sizeof(struct X11VMWRECT) * (cRects - 1), + (struct X11REQHEADER **)&pReqTopology); + pReqTopology->idX11Screen = DefaultScreen(pContext->pDisplay); + pReqTopology->cScreens = cRects; + for (i = 0; i < cRects; ++i) + pReqTopology->aRects[i] = pRects[i]; + _XSend(pContext->pDisplay, NULL, 0); + if (_XReply(pContext->pDisplay, (xReply *)&repTopology, 0, xTrue)) + return; + /* That failed, so try the old single screen set resolution. We prefer + * simpler code to negligeably improved efficiency, so we just always try + * both requests instead of doing version checks or caching. */ + x11GetRequest(pContext, pContext->hVMWMajor, X11_VMW_RESOLUTION_REQUEST, + sizeof(struct X11VMWTOPOLOGYREQ), + (struct X11REQHEADER **)&pReqResolution); + pReqResolution->idX11Screen = DefaultScreen(pContext->pDisplay); + pReqResolution->w = pRects[0].w; + pReqResolution->h = pRects[0].h; + if (_XReply(pContext->pDisplay, (xReply *)&repResolution, 0, xTrue)) + return; + /* What now? */ + VBClFatalError(("%s failed to set resolution\n", __func__)); +} + +/** Call RRGetScreenInfo to wake up the server to the new modes. */ +static void x11GetScreenInfo(struct X11CONTEXT *pContext) +{ + struct X11RANDRGETSCREENREQ *pReqGetScreen; + uint8_t repGetScreen[X11_RANDR_GET_SCREEN_REPLY_SIZE]; + + if (!VALID_PTR(pContext->pDisplay)) + VBClFatalError(("%s bad display argument.\n", __func__)); + x11GetRequest(pContext, pContext->hRandRMajor, X11_RANDR_GET_SCREEN_REQUEST, + sizeof(struct X11RANDRGETSCREENREQ), + (struct X11REQHEADER **)&pReqGetScreen); + pReqGetScreen->hWindow = DefaultRootWindow(pContext->pDisplay); + _XSend(pContext->pDisplay, NULL, 0); + if (!_XReply(pContext->pDisplay, (xReply *)&repGetScreen, 0, xTrue)) + VBClFatalError(("%s failed to set resolution\n", __func__)); +} + +static const char *getPidFilePath() +{ + return ".vboxclient-display-svga-x11.pid"; +} + +static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised) +{ + (void)ppInterface; + (void)fDaemonised; + struct X11CONTEXT x11Context = { NULL }; + unsigned i; + int rc; + uint32_t acx[VMW_MAX_HEADS] = { 0 }; + uint32_t acy[VMW_MAX_HEADS] = { 0 }; + uint32_t adx[VMW_MAX_HEADS] = { 0 }; + uint32_t ady[VMW_MAX_HEADS] = { 0 }; + uint32_t afEnabled[VMW_MAX_HEADS] = { false }; + struct X11VMWRECT aRects[VMW_MAX_HEADS]; + unsigned cHeads; + + if (checkRecentLinuxKernel()) + return VINF_SUCCESS; + x11Connect(&x11Context); + if (x11Context.pDisplay == NULL) + return VINF_SUCCESS; + /* Initialise the guest library. */ + rc = VbglR3InitUser(); + if (RT_FAILURE(rc)) + VBClFatalError(("Failed to connect to the VirtualBox kernel service, rc=%Rrc\n", rc)); + rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0); + if (RT_FAILURE(rc)) + VBClFatalError(("Failed to request display change events, rc=%Rrc\n", rc)); + rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false); + if (rc == VERR_RESOURCE_BUSY) /* Someone else has already acquired it. */ + return VINF_SUCCESS; + if (RT_FAILURE(rc)) + VBClFatalError(("Failed to register resizing support, rc=%Rrc\n", rc)); + for (;;) + { + uint32_t events; + + rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, RT_INDEFINITE_WAIT, &events); + if (RT_FAILURE(rc)) + VBClFatalError(("Failure waiting for event, rc=%Rrc\n", rc)); + while (rc != VERR_TIMEOUT) + { + uint32_t cx, cy, cBits, dx, dy, idx; + bool fEnabled, fChangeOrigin; + + rc = VbglR3GetDisplayChangeRequest(&cx, &cy, &cBits, &idx, &dx, &dy, &fEnabled, &fChangeOrigin, true); + if (RT_FAILURE(rc)) + VBClFatalError(("Failed to get display change request, rc=%Rrc\n", rc)); + if (idx < VMW_MAX_HEADS) + { + acx[idx] = cx; + acy[idx] = cy; + if (fChangeOrigin) + adx[idx] = dx < INT32_MAX ? dx : 0; + if (fChangeOrigin) + ady[idx] = dy < INT32_MAX ? dy : 0; + afEnabled[idx] = fEnabled; + } + rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0, &events); + if (RT_FAILURE(rc) && rc != VERR_TIMEOUT && rc != VERR_INTERRUPTED) + VBClFatalError(("Failure waiting for event, rc=%Rrc\n", rc)); + } + for (i = 0, cHeads = 0; i < VMW_MAX_HEADS; ++i) + { + if (afEnabled[i]) + { + aRects[cHeads].x = (int16_t)adx[i]; + aRects[cHeads].y = (int16_t)ady[i]; + aRects[cHeads].w = (uint16_t)acx[i]; + aRects[cHeads].h = (uint16_t)acy[i]; + ++cHeads; + } + } + x11SendHints(&x11Context, aRects, cHeads); + x11GetScreenInfo(&x11Context); + } +} + +static struct VBCLSERVICE interface = +{ + getPidFilePath, + VBClServiceDefaultHandler, /* Init */ + run, + VBClServiceDefaultCleanup +}, *pInterface = &interface; + +struct VBCLSERVICE **VBClDisplaySVGAX11Service() +{ + return &pInterface; +} diff --git a/src/VBox/Additions/x11/VBoxClient/display-svga.cpp b/src/VBox/Additions/x11/VBoxClient/display-svga.cpp new file mode 100644 index 00000000..15252591 --- /dev/null +++ b/src/VBox/Additions/x11/VBoxClient/display-svga.cpp @@ -0,0 +1,263 @@ +/* $Id: display-svga.cpp $ */ +/** @file + * X11 guest client - VMSVGA emulation resize event pass-through to drm guest + * driver. + */ + +/* + * Copyright (C) 2016-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/* + * Known things to test when changing this code. All assume a guest with VMSVGA + * active and controlled by X11 or Wayland, and Guest Additions installed and + * running, unless otherwise stated. + * - On Linux 4.6 and later guests, VBoxClient --vmsvga should be running as + * root and not as the logged-in user. Dynamic resizing should work for all + * screens in any environment which handles kernel resize notifications, + * including at log-in screens. Test GNOME Shell Wayland and GNOME Shell + * under X.Org or Unity or KDE at the log-in screen and after log-in. + * - Linux 4.10 changed the user-kernel-ABI introduced in 4.6: test both. + * - On other guests (than Linux 4.6 or later) running X.Org Server 1.3 or + * later, VBoxClient --vmsvga should never be running as root, and should run + * (and dynamic resizing and screen enable/disable should work for all + * screens) whenever a user is logged in to a supported desktop environment. + * - On guests running X.Org Server 1.2 or older, VBoxClient --vmsvga should + * never run as root and should run whenever a user is logged in to a + * supported desktop environment. Dynamic resizing should work for the first + * screen, and enabling others should not be possible. + * - When VMSVGA is not enabled, VBoxClient --vmsvga should never stay running. + */ + +#include "VBoxClient.h" + +#include <VBox/VBoxGuestLib.h> + +#include <iprt/assert.h> +#include <iprt/file.h> +#include <iprt/err.h> +#include <iprt/string.h> + +/** Maximum number of supported screens. DRM and X11 both limit this to 32. */ +/** @todo if this ever changes, dynamically allocate resizeable arrays in the + * context structure. */ +#define VMW_MAX_HEADS 32 + +/* VMWare kernel driver control parts definitions. */ + +#ifdef RT_OS_LINUX +# include <sys/ioctl.h> +#else /* Solaris and BSDs, in case they ever adopt the DRM driver. */ +# include <sys/ioccom.h> +#endif + +#define DRM_DRIVER_NAME "vmwgfx" + +/** DRM version structure. */ +struct DRMVERSION +{ + int cMajor; + int cMinor; + int cPatchLevel; + size_t cbName; + char *pszName; + size_t cbDate; + char *pszDate; + size_t cbDescription; + char *pszDescription; +}; +AssertCompileSize(struct DRMVERSION, 8 + 7 * sizeof(void *)); + +/** Rectangle structure for geometry of a single screen. */ +struct DRMVMWRECT +{ + int32_t x; + int32_t y; + uint32_t w; + uint32_t h; +}; +AssertCompileSize(struct DRMVMWRECT, 16); + +#define DRM_IOCTL_VERSION _IOWR('d', 0x00, struct DRMVERSION) + +struct DRMCONTEXT +{ + RTFILE hDevice; +}; + +static void drmConnect(struct DRMCONTEXT *pContext) +{ + unsigned i; + RTFILE hDevice; + + if (pContext->hDevice != NIL_RTFILE) + VBClFatalError(("%s called with bad argument\n", __func__)); + /* Try to open the SVGA DRM device. */ + for (i = 0; i < 128; ++i) + { + char szPath[64]; + struct DRMVERSION version; + char szName[sizeof(DRM_DRIVER_NAME)]; + int rc; + + /* Control devices for drm graphics driver control devices go from + * controlD64 to controlD127. Render node devices go from renderD128 + * to renderD192. The driver takes resize hints via the control device + * on pre-4.10 kernels and on the render device on newer ones. Try + * both types. */ + if (i % 2 == 0) + rc = RTStrPrintf(szPath, sizeof(szPath), "/dev/dri/renderD%u", i / 2 + 128); + else + rc = RTStrPrintf(szPath, sizeof(szPath), "/dev/dri/controlD%u", i / 2 + 64); + if (RT_FAILURE(rc)) + VBClFatalError(("RTStrPrintf of device path failed, rc=%Rrc\n", rc)); + rc = RTFileOpen(&hDevice, szPath, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); + if (RT_FAILURE(rc)) + continue; + RT_ZERO(version); + version.cbName = sizeof(szName); + version.pszName = szName; + rc = RTFileIoCtl(hDevice, DRM_IOCTL_VERSION, &version, sizeof(version), NULL); + if ( RT_SUCCESS(rc) + && !strncmp(szName, DRM_DRIVER_NAME, sizeof(DRM_DRIVER_NAME) - 1) + && ( version.cMajor > 2 + || (version.cMajor == 2 && version.cMinor > 9))) + break; + hDevice = NIL_RTFILE; + } + pContext->hDevice = hDevice; +} + +/** Preferred screen layout information for DRM_VMW_UPDATE_LAYOUT IoCtl. The + * rects argument is a cast pointer to an array of drm_vmw_rect. */ +struct DRMVMWUPDATELAYOUT { + uint32_t cOutputs; + uint32_t u32Pad; + uint64_t ptrRects; +}; +AssertCompileSize(struct DRMVMWUPDATELAYOUT, 16); + +#define DRM_IOCTL_VMW_UPDATE_LAYOUT \ + _IOW('d', 0x40 + 20, struct DRMVMWUPDATELAYOUT) + +static void drmSendHints(struct DRMCONTEXT *pContext, struct DRMVMWRECT *paRects, + unsigned cHeads) +{ + int rc; + struct DRMVMWUPDATELAYOUT ioctlLayout; + + if (pContext->hDevice == NIL_RTFILE) + VBClFatalError(("%s bad device argument.\n", __func__)); + ioctlLayout.cOutputs = cHeads; + ioctlLayout.ptrRects = (uint64_t)paRects; + rc = RTFileIoCtl(pContext->hDevice, DRM_IOCTL_VMW_UPDATE_LAYOUT, + &ioctlLayout, sizeof(ioctlLayout), NULL); + if (RT_FAILURE(rc) && rc != VERR_INVALID_PARAMETER) + VBClFatalError(("Failure updating layout, rc=%Rrc\n", rc)); +} + +static const char *getPidFilePath() +{ + return ".vboxclient-display-svga.pid"; +} + +static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised) +{ + (void)ppInterface; + (void)fDaemonised; + struct DRMCONTEXT drmContext = { NIL_RTFILE }; + unsigned i; + int rc; + uint32_t acx[VMW_MAX_HEADS] = { 0 }; + uint32_t acy[VMW_MAX_HEADS] = { 0 }; + uint32_t adx[VMW_MAX_HEADS] = { 0 }; + uint32_t ady[VMW_MAX_HEADS] = { 0 }; + uint32_t afEnabled[VMW_MAX_HEADS] = { false }; + struct DRMVMWRECT aRects[VMW_MAX_HEADS]; + unsigned cHeads; + + drmConnect(&drmContext); + if (drmContext.hDevice == NIL_RTFILE) + return VINF_SUCCESS; + /* Initialise the guest library. */ + rc = VbglR3InitUser(); + if (RT_FAILURE(rc)) + VBClFatalError(("Failed to connect to the VirtualBox kernel service, rc=%Rrc\n", rc)); + rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0); + if (RT_FAILURE(rc)) + VBClFatalError(("Failed to request display change events, rc=%Rrc\n", rc)); + rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false); + if (rc == VERR_RESOURCE_BUSY) /* Someone else has already acquired it. */ + return VINF_SUCCESS; + if (RT_FAILURE(rc)) + VBClFatalError(("Failed to register resizing support, rc=%Rrc\n", rc)); + for (;;) + { + uint32_t events; + + rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, RT_INDEFINITE_WAIT, &events); + if (RT_FAILURE(rc)) + VBClFatalError(("Failure waiting for event, rc=%Rrc\n", rc)); + while (rc != VERR_TIMEOUT) + { + uint32_t cx, cy, cBits, dx, dy, idx; + bool fEnabled, fChangeOrigin; + + rc = VbglR3GetDisplayChangeRequest(&cx, &cy, &cBits, &idx, &dx, &dy, &fEnabled, &fChangeOrigin, true); + if (RT_FAILURE(rc)) + VBClFatalError(("Failed to get display change request, rc=%Rrc\n", rc)); + if (idx < VMW_MAX_HEADS) + { + acx[idx] = cx; + acy[idx] = cy; + if (fChangeOrigin) + adx[idx] = dx < INT32_MAX ? dx : 0; + if (fChangeOrigin) + ady[idx] = dy < INT32_MAX ? dy : 0; + afEnabled[idx] = fEnabled; + } + rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0, &events); + if (RT_FAILURE(rc) && rc != VERR_TIMEOUT && rc != VERR_INTERRUPTED) + VBClFatalError(("Failure waiting for event, rc=%Rrc\n", rc)); + } + for (i = 0, cHeads = 0; i < VMW_MAX_HEADS; ++i) + { + if (afEnabled[i]) + { + if ((i == 0) || (adx[i] || ady[i])) + { + aRects[cHeads].x = (int32_t)adx[i]; + aRects[cHeads].y = (int32_t)ady[i]; + } else { + aRects[cHeads].x = (int32_t)(adx[i - 1] + acx[i - 1]); + aRects[cHeads].y = (int32_t)ady[i - 1]; + } + aRects[cHeads].w = acx[i]; + aRects[cHeads].h = acy[i]; + ++cHeads; + } + } + drmSendHints(&drmContext, aRects, cHeads); + } +} + +static struct VBCLSERVICE interface = +{ + getPidFilePath, + VBClServiceDefaultHandler, /* Init */ + run, + VBClServiceDefaultCleanup +}, *pInterface = &interface; + +struct VBCLSERVICE **VBClDisplaySVGAService() +{ + return &pInterface; +} diff --git a/src/VBox/Additions/x11/VBoxClient/display.cpp b/src/VBox/Additions/x11/VBoxClient/display.cpp new file mode 100644 index 00000000..6a168a2a --- /dev/null +++ b/src/VBox/Additions/x11/VBoxClient/display.cpp @@ -0,0 +1,281 @@ +/* $Id: display.cpp $ */ +/** @file + * X11 guest client - display management. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include "VBoxClient.h" + +#include <iprt/errcore.h> +#include <iprt/file.h> +#include <iprt/mem.h> +#include <iprt/string.h> + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/extensions/Xrandr.h> + +/** @todo this should probably be replaced by something IPRT */ +/* For system() and WEXITSTATUS() */ +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <limits.h> +#include <poll.h> +#include <time.h> + +/* TESTING: Dynamic resizing and mouse integration toggling should work + * correctly with a range of X servers (pre-1.3, 1.3 and later under Linux, 1.3 + * and later under Solaris) with Guest Additions installed. Switching to a + * virtual terminal while a user session is in place should disable dynamic + * resizing and cursor integration, switching back should re-enable them. */ + +/** Display magic number, start of a UUID. */ +#define DISPLAYSTATE_MAGIC UINT32_C(0xf0029993) + +/** State information needed for the service. The main VBoxClient code provides + * the daemon logic needed by all services. */ +struct DISPLAYSTATE +{ + /** The service interface. */ + struct VBCLSERVICE *pInterface; + /** Magic number for sanity checks. */ + uint32_t magic; + /** Are we initialised yet? */ + bool mfInit; + /** The connection to the server. */ + Display *pDisplay; + /** The RandR extension base event number. */ + int cRREventBase; + /** Can we use version 1.2 or later of the RandR protocol here? */ + bool fHaveRandR12; + /** The command argument to use for the xrandr binary. Currently only + * used to support the non-standard location on some Solaris systems - + * would it make sense to use absolute paths on all systems? */ + const char *pcszXrandr; + /** Was there a recent mode hint with no following root window resize, and + * if so, have we waited for a reasonable time? */ + time_t timeLastModeHint; +}; + +static unsigned char *getRootProperty(struct DISPLAYSTATE *pState, const char *pszName, + long cItems, Atom type) +{ + Atom actualType = None; + int iFormat = 0; + unsigned long cReturned = 0; + unsigned long cAfter = 0; + unsigned char *pData = 0; + + if (XGetWindowProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay), + XInternAtom(pState->pDisplay, pszName, 0), 0, cItems, + False /* delete */, type, &actualType, &iFormat, + &cReturned, &cAfter, &pData)) + return NULL; + return pData; +} + +static void doResize(struct DISPLAYSTATE *pState) +{ + /** @note The xrandr command can fail if something else accesses RandR at + * the same time. We just ignore failure for now as we do not know what + * someone else is doing. */ + if (!pState->fHaveRandR12) + { + char szCommand[256]; + unsigned char *pData; + + pData = getRootProperty(pState, "VBOXVIDEO_PREFERRED_MODE", 1, XA_INTEGER); + if (pData != NULL) + { + RTStrPrintf(szCommand, sizeof(szCommand), "%s -s %ux%u", + pState->pcszXrandr, ((unsigned long *)pData)[0] >> 16, ((unsigned long *)pData)[0] & 0xFFFF); + int rcShutUpGcc = system(szCommand); RT_NOREF_PV(rcShutUpGcc); + XFree(pData); + } + } + else + { + const char szCommandBase[] = + "%s --output VGA-0 --auto --output VGA-1 --auto --right-of VGA-0 " + "--output VGA-2 --auto --right-of VGA-1 --output VGA-3 --auto --right-of VGA-2 " + "--output VGA-4 --auto --right-of VGA-3 --output VGA-5 --auto --right-of VGA-4 " + "--output VGA-6 --auto --right-of VGA-5 --output VGA-7 --auto --right-of VGA-6 " + "--output VGA-8 --auto --right-of VGA-7 --output VGA-9 --auto --right-of VGA-8 " + "--output VGA-10 --auto --right-of VGA-9 --output VGA-11 --auto --right-of VGA-10 " + "--output VGA-12 --auto --right-of VGA-11 --output VGA-13 --auto --right-of VGA-12 " + "--output VGA-14 --auto --right-of VGA-13 --output VGA-15 --auto --right-of VGA-14 " + "--output VGA-16 --auto --right-of VGA-15 --output VGA-17 --auto --right-of VGA-16 " + "--output VGA-18 --auto --right-of VGA-17 --output VGA-19 --auto --right-of VGA-18 " + "--output VGA-20 --auto --right-of VGA-19 --output VGA-21 --auto --right-of VGA-20 " + "--output VGA-22 --auto --right-of VGA-21 --output VGA-23 --auto --right-of VGA-22 " + "--output VGA-24 --auto --right-of VGA-23 --output VGA-25 --auto --right-of VGA-24 " + "--output VGA-26 --auto --right-of VGA-25 --output VGA-27 --auto --right-of VGA-26 " + "--output VGA-28 --auto --right-of VGA-27 --output VGA-29 --auto --right-of VGA-28 " + "--output VGA-30 --auto --right-of VGA-29 --output VGA-31 --auto --right-of VGA-30"; + char szCommand[sizeof(szCommandBase) + 256]; + RTStrPrintf(szCommand, sizeof(szCommand), szCommandBase, pState->pcszXrandr); + int rcShutUpGcc = system(szCommand); RT_NOREF_PV(rcShutUpGcc); + } +} + +/** Main loop: handle display hot-plug events, property updates (which can + * signal VT switches hot-plug in old X servers). */ +static void runDisplay(struct DISPLAYSTATE *pState) +{ + Display *pDisplay = pState->pDisplay; + long cValue = 1; + + /* One way or another we want the preferred mode at server start-up. */ + doResize(pState); + XSelectInput(pDisplay, DefaultRootWindow(pDisplay), PropertyChangeMask | StructureNotifyMask); + if (pState->fHaveRandR12) + XRRSelectInput(pDisplay, DefaultRootWindow(pDisplay), RRScreenChangeNotifyMask); + /* Semantics: when VBOXCLIENT_STARTED is set, pre-1.3 X.Org Server driver + * assumes that a client capable of handling mode hints will be present for the + * rest of the X session. If we crash things will not work as they should. + * I thought that preferable to implementing complex crash-handling logic. + */ + XChangeProperty(pState->pDisplay, DefaultRootWindow(pState->pDisplay), XInternAtom(pState->pDisplay, "VBOXCLIENT_STARTED", 0), + XA_INTEGER, 32, PropModeReplace, (unsigned char *)&cValue, 1); + /* Interrupting this cleanly will be more work than making it robust + * against spontaneous termination, especially as it will never get + * properly tested, so I will go for the second. */ + while (true) + { + XEvent event; + struct pollfd PollFd; + int pollTimeOut = -1; + int cFds; + + /* Do not handle overflow. */ + if (pState->timeLastModeHint > 0 && pState->timeLastModeHint < INT_MAX - 2) + pollTimeOut = 2 - (time(0) - pState->timeLastModeHint); + PollFd.fd = ConnectionNumber(pDisplay); + PollFd.events = POLLIN; /* Hang-up is always reported. */ + XFlush(pDisplay); + cFds = poll(&PollFd, 1, pollTimeOut >= 0 ? pollTimeOut * 1000 : -1); + while (XPending(pDisplay)) + { + XNextEvent(pDisplay, &event); + /* This property is deleted when the server regains the virtual + * terminal. Force the main thread to call xrandr again, as old X + * servers could not handle it while switched out. */ + if ( !pState->fHaveRandR12 + && event.type == PropertyNotify + && event.xproperty.state == PropertyDelete + && event.xproperty.window == DefaultRootWindow(pDisplay) + && event.xproperty.atom == XInternAtom(pDisplay, "VBOXVIDEO_NO_VT", False)) + doResize(pState); + if ( !pState->fHaveRandR12 + && event.type == PropertyNotify + && event.xproperty.state == PropertyNewValue + && event.xproperty.window == DefaultRootWindow(pDisplay) + && event.xproperty.atom == XInternAtom(pDisplay, "VBOXVIDEO_PREFERRED_MODE", False)) + doResize(pState); + if ( pState->fHaveRandR12 + && event.type == pState->cRREventBase + RRScreenChangeNotify) + pState->timeLastModeHint = time(0); + if ( event.type == ConfigureNotify + && event.xproperty.window == DefaultRootWindow(pDisplay)) + pState->timeLastModeHint = 0; + } + if (cFds == 0 && pState->timeLastModeHint > 0) + doResize(pState); + } +} + +static int initDisplay(struct DISPLAYSTATE *pState) +{ + char szCommand[256]; + int status; + + pState->pDisplay = XOpenDisplay(NULL); + if (!pState->pDisplay) + return VERR_NOT_FOUND; + if (!XRRQueryExtension(pState->pDisplay, &pState->cRREventBase, &status)) + return VERR_NOT_FOUND; + pState->fHaveRandR12 = false; + pState->pcszXrandr = "xrandr"; + if (RTFileExists("/usr/X11/bin/xrandr")) + pState->pcszXrandr = "/usr/X11/bin/xrandr"; + status = system(pState->pcszXrandr); + if (WEXITSTATUS(status) != 0) /* Utility or extension not available. */ + VBClFatalError(("Failed to execute the xrandr utility.\n")); + RTStrPrintf(szCommand, sizeof(szCommand), "%s --q12", pState->pcszXrandr); + status = system(szCommand); + if (WEXITSTATUS(status) == 0) + pState->fHaveRandR12 = true; + return VINF_SUCCESS; +} + +static const char *getPidFilePath() +{ + return ".vboxclient-display.pid"; +} + +static struct DISPLAYSTATE *getStateFromInterface(struct VBCLSERVICE **ppInterface) +{ + struct DISPLAYSTATE *pSelf = (struct DISPLAYSTATE *)ppInterface; + if (pSelf->magic != DISPLAYSTATE_MAGIC) + VBClFatalError(("Bad display service object!\n")); + return pSelf; +} + +static int init(struct VBCLSERVICE **ppInterface) +{ + struct DISPLAYSTATE *pSelf = getStateFromInterface(ppInterface); + int rc; + + if (pSelf->mfInit) + return VERR_WRONG_ORDER; + rc = initDisplay(pSelf); + if (RT_FAILURE(rc)) + return rc; + if (RT_SUCCESS(rc)) + pSelf->mfInit = true; + return rc; +} + +static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised) +{ + RT_NOREF1(fDaemonised); + struct DISPLAYSTATE *pSelf = getStateFromInterface(ppInterface); + + if (!pSelf->mfInit) + return VERR_WRONG_ORDER; + runDisplay(pSelf); + return VERR_INTERNAL_ERROR; /* "Should never reach here." */ +} + +struct VBCLSERVICE vbclDisplayInterface = +{ + getPidFilePath, + init, + run, + VBClServiceDefaultCleanup +}; + +struct VBCLSERVICE **VBClGetDisplayService() +{ + struct DISPLAYSTATE *pService = (struct DISPLAYSTATE *)RTMemAlloc(sizeof(*pService)); + + if (!pService) + VBClFatalError(("Out of memory\n")); + pService->pInterface = &vbclDisplayInterface; + pService->magic = DISPLAYSTATE_MAGIC; + pService->mfInit = false; + return &pService->pInterface; +} diff --git a/src/VBox/Additions/x11/VBoxClient/draganddrop.cpp b/src/VBox/Additions/x11/VBoxClient/draganddrop.cpp new file mode 100644 index 00000000..7ebac8ea --- /dev/null +++ b/src/VBox/Additions/x11/VBoxClient/draganddrop.cpp @@ -0,0 +1,3547 @@ +/* $Id: draganddrop.cpp $ */ +/** @file + * X11 guest client - Drag and drop implementation. + */ + +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> +#ifdef VBOX_DND_WITH_XTEST +# include <X11/extensions/XTest.h> +#endif + +#include <iprt/asm.h> +#include <iprt/buildconfig.h> +#include <iprt/critsect.h> +#include <iprt/thread.h> +#include <iprt/time.h> + +#include <iprt/cpp/mtlist.h> +#include <iprt/cpp/ministring.h> + +#include <limits.h> + +#ifdef LOG_GROUP +# undef LOG_GROUP +#endif +#define LOG_GROUP LOG_GROUP_GUEST_DND +#include <VBox/log.h> +#include <VBox/VBoxGuestLib.h> + +#include "VBox/HostServices/DragAndDropSvc.h" +#include "VBoxClient.h" + +/* Enable this define to see the proxy window(s) when debugging + * their behavior. Don't have this enabled in release builds! */ +#ifdef DEBUG +//# define VBOX_DND_DEBUG_WND +#endif + +/** + * For X11 guest Xdnd is used. See http://www.acc.umu.se/~vatten/XDND.html for + * a walk trough. + * + * Host -> Guest: + * For X11 this means mainly forwarding all the events from HGCM to the + * appropriate X11 events. There exists a proxy window, which is invisible and + * used for all the X11 communication. On a HGCM Enter event, we set our proxy + * window as XdndSelection owner with the given mime-types. On every HGCM move + * event, we move the X11 mouse cursor to the new position and query for the + * window below that position. Depending on if it is XdndAware, a new window or + * a known window, we send the appropriate X11 messages to it. On HGCM drop, we + * send a XdndDrop message to the current window and wait for a X11 + * SelectionMessage from the target window. Because we didn't have the data in + * the requested mime-type, yet, we save that message and ask the host for the + * data. When the data is successfully received from the host, we put the data + * as a property to the window and send a X11 SelectionNotify event to the + * target window. + * + * Guest -> Host: + * This is a lot more trickery than H->G. When a pending event from HGCM + * arrives, we ask if there currently is an owner of the XdndSelection + * property. If so, our proxy window is shown (1x1, but without backing store) + * and some mouse event is triggered. This should be followed by an XdndEnter + * event send to the proxy window. From this event we can fetch the necessary + * info of the MIME types and allowed actions and send this back to the host. + * On a drop request from the host, we query for the selection and should get + * the data in the specified mime-type. This data is send back to the host. + * After that we send a XdndLeave event to the source window. + * + ** @todo Cancelling (e.g. with ESC key) doesn't work. + ** @todo INCR (incremental transfers) support. + ** @todo Really check for the Xdnd version and the supported features. + ** @todo Either get rid of the xHelpers class or properly unify the code with the drag instance class. + */ + +#define VBOX_XDND_VERSION (4) +#define VBOX_MAX_XPROPERTIES (LONG_MAX-1) + +/** + * Structure for storing new X11 events and HGCM messages + * into a single event queue. + */ +struct DnDEvent +{ + enum DnDEventType + { + /** Unknown event, do not use. */ + DnDEventType_Unknown = 0, + /** VBGLR3DNDEVENT event. */ + DnDEventType_HGCM, + /** X11 event. */ + DnDEventType_X11, + /** Blow the type up to 32-bit. */ + DnDEventType_32BIT_HACK = 0x7fffffff + }; + /** Event type. */ + DnDEventType enmType; + union + { + PVBGLR3DNDEVENT hgcm; + XEvent x11; + }; +}; + +enum XA_Type +{ + /* States */ + XA_WM_STATE = 0, + /* Properties */ + XA_TARGETS, + XA_MULTIPLE, + XA_INCR, + /* Mime Types */ + XA_image_bmp, + XA_image_jpg, + XA_image_tiff, + XA_image_png, + XA_text_uri_list, + XA_text_uri, + XA_text_plain, + XA_TEXT, + /* Xdnd */ + XA_XdndSelection, + XA_XdndAware, + XA_XdndEnter, + XA_XdndLeave, + XA_XdndTypeList, + XA_XdndActionList, + XA_XdndPosition, + XA_XdndActionCopy, + XA_XdndActionMove, + XA_XdndActionLink, + XA_XdndStatus, + XA_XdndDrop, + XA_XdndFinished, + /* Our own stop marker */ + XA_dndstop, + /* End marker */ + XA_End +}; + +/** + * Xdnd message value indexes, sorted by message type. + */ +typedef enum XdndMsg +{ + /** XdndEnter. */ + XdndEnterTypeCount = 3, /* Maximum number of types in XdndEnter message. */ + + XdndEnterWindow = 0, /* Source window (sender). */ + XdndEnterFlags, /* Version in high byte, bit 0 => more data types. */ + XdndEnterType1, /* First available data type. */ + XdndEnterType2, /* Second available data type. */ + XdndEnterType3, /* Third available data type. */ + + XdndEnterMoreTypesFlag = 1, /* Set if there are more than XdndEnterTypeCount. */ + XdndEnterVersionRShift = 24, /* Right shift to position version number. */ + XdndEnterVersionMask = 0xFF, /* Mask to get version after shifting. */ + + /** XdndHere. */ + XdndHereWindow = 0, /* Source window (sender). */ + XdndHereFlags, /* Reserved. */ + XdndHerePt, /* X + Y coordinates of mouse (root window coords). */ + XdndHereTimeStamp, /* Timestamp for requesting data. */ + XdndHereAction, /* Action requested by user. */ + + /** XdndPosition. */ + XdndPositionWindow = 0, /* Source window (sender). */ + XdndPositionFlags, /* Flags. */ + XdndPositionXY, /* X/Y coordinates of the mouse position relative to the root window. */ + XdndPositionTimeStamp, /* Time stamp for retrieving the data. */ + XdndPositionAction, /* Action requested by the user. */ + + /** XdndStatus. */ + XdndStatusWindow = 0, /* Target window (sender).*/ + XdndStatusFlags, /* Flags returned by target. */ + XdndStatusNoMsgXY, /* X + Y of "no msg" rectangle (root window coords). */ + XdndStatusNoMsgWH, /* Width + height of "no msg" rectangle. */ + XdndStatusAction, /* Action accepted by target. */ + + XdndStatusAcceptDropFlag = 1, /* Set if target will accept the drop. */ + XdndStatusSendHereFlag = 2, /* Set if target wants a stream of XdndPosition. */ + + /** XdndLeave. */ + XdndLeaveWindow = 0, /* Source window (sender). */ + XdndLeaveFlags, /* Reserved. */ + + /** XdndDrop. */ + XdndDropWindow = 0, /* Source window (sender). */ + XdndDropFlags, /* Reserved. */ + XdndDropTimeStamp, /* Timestamp for requesting data. */ + + /** XdndFinished. */ + XdndFinishedWindow = 0, /* Target window (sender). */ + XdndFinishedFlags, /* Version 5: Bit 0 is set if the current target accepted the drop. */ + XdndFinishedAction /* Version 5: Contains the action performed by the target. */ + +} XdndMsg; + +class DragAndDropService; + +/** List of Atoms. */ +#define VBoxDnDAtomList RTCList<Atom> + +/******************************************************************************* + * + * xHelpers Declaration + * + ******************************************************************************/ + +class xHelpers +{ +public: + + static xHelpers *getInstance(Display *pDisplay = 0) + { + if (!m_pInstance) + { + AssertPtrReturn(pDisplay, NULL); + m_pInstance = new xHelpers(pDisplay); + } + + return m_pInstance; + } + + static void destroyInstance(void) + { + if (m_pInstance) + { + delete m_pInstance; + m_pInstance = NULL; + } + } + + inline Display *display() const { return m_pDisplay; } + inline Atom xAtom(XA_Type e) const { return m_xAtoms[e]; } + + inline Atom stringToxAtom(const char *pcszString) const + { + return XInternAtom(m_pDisplay, pcszString, False); + } + inline RTCString xAtomToString(Atom atom) const + { + if (atom == None) return "None"; + + char* pcsAtom = XGetAtomName(m_pDisplay, atom); + RTCString strAtom(pcsAtom); + XFree(pcsAtom); + + return strAtom; + } + + inline RTCString xAtomListToString(const VBoxDnDAtomList &formatList) + { + RTCString format; + for (size_t i = 0; i < formatList.size(); ++i) + format += xAtomToString(formatList.at(i)) + "\r\n"; + return format; + } + + RTCString xErrorToString(int xRc) const; + Window applicationWindowBelowCursor(Window parentWin) const; + +private: + + xHelpers(Display *pDisplay) + : m_pDisplay(pDisplay) + { + /* Not all x11 atoms we use are defined in the headers. Create the + * additional one we need here. */ + for (int i = 0; i < XA_End; ++i) + m_xAtoms[i] = XInternAtom(m_pDisplay, m_xAtomNames[i], False); + }; + + /* Private member vars */ + static xHelpers *m_pInstance; + Display *m_pDisplay; + Atom m_xAtoms[XA_End]; + static const char *m_xAtomNames[XA_End]; +}; + +/* Some xHelpers convenience defines. */ +#define gX11 xHelpers::getInstance() +#define xAtom(xa) xHelpers::getInstance()->xAtom((xa)) +#define xAtomToString(xa) xHelpers::getInstance()->xAtomToString((xa)) + +/******************************************************************************* + * + * xHelpers Implementation + * + ******************************************************************************/ + +xHelpers *xHelpers::m_pInstance = NULL; + +/* Has to be in sync with the XA_Type enum. */ +const char *xHelpers::m_xAtomNames[] = +{ + /* States */ + "WM_STATE", + /* Properties */ + "TARGETS", + "MULTIPLE", + "INCR", + /* Mime Types */ + "image/bmp", + "image/jpg", + "image/tiff", + "image/png", + "text/uri-list", + "text/uri", + "text/plain", + "TEXT", + /* Xdnd */ + "XdndSelection", + "XdndAware", + "XdndEnter", + "XdndLeave", + "XdndTypeList", + "XdndActionList", + "XdndPosition", + "XdndActionCopy", + "XdndActionMove", + "XdndActionLink", + "XdndStatus", + "XdndDrop", + "XdndFinished", + /* Our own stop marker */ + "dndstop" +}; + +RTCString xHelpers::xErrorToString(int xRc) const +{ + switch (xRc) + { + case Success: return RTCStringFmt("%d (Success)", xRc); break; + case BadRequest: return RTCStringFmt("%d (BadRequest)", xRc); break; + case BadValue: return RTCStringFmt("%d (BadValue)", xRc); break; + case BadWindow: return RTCStringFmt("%d (BadWindow)", xRc); break; + case BadPixmap: return RTCStringFmt("%d (BadPixmap)", xRc); break; + case BadAtom: return RTCStringFmt("%d (BadAtom)", xRc); break; + case BadCursor: return RTCStringFmt("%d (BadCursor)", xRc); break; + case BadFont: return RTCStringFmt("%d (BadFont)", xRc); break; + case BadMatch: return RTCStringFmt("%d (BadMatch)", xRc); break; + case BadDrawable: return RTCStringFmt("%d (BadDrawable)", xRc); break; + case BadAccess: return RTCStringFmt("%d (BadAccess)", xRc); break; + case BadAlloc: return RTCStringFmt("%d (BadAlloc)", xRc); break; + case BadColor: return RTCStringFmt("%d (BadColor)", xRc); break; + case BadGC: return RTCStringFmt("%d (BadGC)", xRc); break; + case BadIDChoice: return RTCStringFmt("%d (BadIDChoice)", xRc); break; + case BadName: return RTCStringFmt("%d (BadName)", xRc); break; + case BadLength: return RTCStringFmt("%d (BadLength)", xRc); break; + case BadImplementation: return RTCStringFmt("%d (BadImplementation)", xRc); break; + } + return RTCStringFmt("%d (unknown)", xRc); +} + +/** @todo Make this iterative. */ +Window xHelpers::applicationWindowBelowCursor(Window wndParent) const +{ + /* No parent, nothing to do. */ + if(wndParent == 0) + return 0; + + Window wndApp = 0; + int cProps = -1; + + /* Fetch all x11 window properties of the parent window. */ + Atom *pProps = XListProperties(m_pDisplay, wndParent, &cProps); + if (cProps > 0) + { + /* We check the window for the WM_STATE property. */ + for (int i = 0; i < cProps; ++i) + { + if (pProps[i] == xAtom(XA_WM_STATE)) + { + /* Found it. */ + wndApp = wndParent; + break; + } + } + + /* Cleanup */ + XFree(pProps); + } + + if (!wndApp) + { + Window wndChild, wndTemp; + int tmp; + unsigned int utmp; + + /* Query the next child window of the parent window at the current + * mouse position. */ + XQueryPointer(m_pDisplay, wndParent, &wndTemp, &wndChild, &tmp, &tmp, &tmp, &tmp, &utmp); + + /* Recursive call our self to dive into the child tree. */ + wndApp = applicationWindowBelowCursor(wndChild); + } + + return wndApp; +} + +/******************************************************************************* + * + * DragInstance Declaration + * + ******************************************************************************/ + +#ifdef DEBUG +# define VBOX_DND_FN_DECL_LOG(x) inline x /* For LogFlowXXX logging. */ +#else +# define VBOX_DND_FN_DECL_LOG(x) x +#endif + +/** @todo Move all proxy window-related stuff into this class! Clean up this mess. */ +class VBoxDnDProxyWnd +{ + +public: + + VBoxDnDProxyWnd(void); + + virtual ~VBoxDnDProxyWnd(void); + +public: + + int init(Display *pDisplay); + void destroy(); + + int sendFinished(Window hWndSource, VBOXDNDACTION dndAction); + +public: + + Display *pDisp; + /** Proxy window handle. */ + Window hWnd; + int iX; + int iY; + int iWidth; + int iHeight; +}; + +/** + * Class for handling a single drag and drop operation, that is, + * one source and one target at a time. + * + * For now only one DragInstance will exits when the app is running. + */ +class DragInstance +{ +public: + + enum State + { + Uninitialized = 0, + Initialized, + Dragging, + Dropped, + State_32BIT_Hack = 0x7fffffff + }; + + enum Mode + { + Unknown = 0, + HG, + GH, + Mode_32Bit_Hack = 0x7fffffff + }; + + DragInstance(Display *pDisplay, DragAndDropService *pParent); + +public: + + int init(uint32_t uScreenID); + void uninit(void); + void reset(void); + + /* Logging. */ + VBOX_DND_FN_DECL_LOG(void) logInfo(const char *pszFormat, ...); + VBOX_DND_FN_DECL_LOG(void) logError(const char *pszFormat, ...); + + /* X11 message processing. */ + int onX11ClientMessage(const XEvent &e); + int onX11MotionNotify(const XEvent &e); + int onX11SelectionClear(const XEvent &e); + int onX11SelectionNotify(const XEvent &e); + int onX11SelectionRequest(const XEvent &e); + int onX11Event(const XEvent &e); + int waitForStatusChange(uint32_t enmState, RTMSINTERVAL uTimeoutMS = 30000); + bool waitForX11Msg(XEvent &evX, int iType, RTMSINTERVAL uTimeoutMS = 100); + bool waitForX11ClientMsg(XClientMessageEvent &evMsg, Atom aType, RTMSINTERVAL uTimeoutMS = 100); + + /* Session handling. */ + int checkForSessionChange(void); + +#ifdef VBOX_WITH_DRAG_AND_DROP_GH + /* Guest -> Host handling. */ + int ghIsDnDPending(void); + int ghDropped(const RTCString &strFormat, VBOXDNDACTION dndActionRequested); +#endif + + /* Host -> Guest handling. */ + int hgEnter(const RTCList<RTCString> &formats, VBOXDNDACTIONLIST dndListActionsAllowed); + int hgLeave(void); + int hgMove(uint32_t uPosX, uint32_t uPosY, VBOXDNDACTION dndActionDefault); + int hgDrop(uint32_t uPosX, uint32_t uPosY, VBOXDNDACTION dndActionDefault); + int hgDataReceive(PVBGLR3GUESTDNDMETADATA pMetaData); + + /* X11 helpers. */ + int mouseCursorFakeMove(void) const; + int mouseCursorMove(int iPosX, int iPosY) const; + void mouseButtonSet(Window wndDest, int rx, int ry, int iButton, bool fPress); + int proxyWinShow(int *piRootX = NULL, int *piRootY = NULL) const; + int proxyWinHide(void); + + /* X11 window helpers. */ + char *wndX11GetNameA(Window wndThis) const; + + /* Xdnd protocol helpers. */ + void wndXDnDClearActionList(Window wndThis) const; + void wndXDnDClearFormatList(Window wndThis) const; + int wndXDnDGetActionList(Window wndThis, VBoxDnDAtomList &lstActions) const; + int wndXDnDGetFormatList(Window wndThis, VBoxDnDAtomList &lstTypes) const; + int wndXDnDSetActionList(Window wndThis, const VBoxDnDAtomList &lstActions) const; + int wndXDnDSetFormatList(Window wndThis, Atom atmProp, const VBoxDnDAtomList &lstFormats) const; + + /* Atom / HGCM formatting helpers. */ + int toAtomList(const RTCList<RTCString> &lstFormats, VBoxDnDAtomList &lstAtoms) const; + int toAtomList(const void *pvData, uint32_t cbData, VBoxDnDAtomList &lstAtoms) const; + static Atom toAtomAction(VBOXDNDACTION dndAction); + static int toAtomActions(VBOXDNDACTIONLIST dndActionList, VBoxDnDAtomList &lstAtoms); + static uint32_t toHGCMAction(Atom atom); + static uint32_t toHGCMActions(const VBoxDnDAtomList &actionsList); + +protected: + + /** The instance's own DnD context. */ + VBGLR3GUESTDNDCMDCTX m_dndCtx; + /** Pointer to service instance. */ + DragAndDropService *m_pParent; + /** Pointer to X display operating on. */ + Display *m_pDisplay; + /** X screen ID to operate on. */ + int m_screenID; + /** Pointer to X screen operating on. */ + Screen *m_pScreen; + /** Root window handle. */ + Window m_wndRoot; + /** Proxy window. */ + VBoxDnDProxyWnd m_wndProxy; + /** Current source/target window handle. */ + Window m_wndCur; + /** The XDnD protocol version the current + * source/target window is using. */ + long m_curVer; + /** List of (Atom) formats the source window supports. */ + VBoxDnDAtomList m_lstFormats; + /** List of (Atom) actions the source window supports. */ + VBoxDnDAtomList m_lstActions; + /** Buffer for answering the target window's selection request. */ + void *m_pvSelReqData; + /** Size (in bytes) of selection request data buffer. */ + uint32_t m_cbSelReqData; + /** Current operation mode. */ + volatile uint32_t m_enmMode; + /** Current state of operation mode. */ + volatile uint32_t m_enmState; + /** The instance's own X event queue. */ + RTCMTList<XEvent> m_eventQueueList; + /** Critical section for providing serialized access to list + * event queue's contents. */ + RTCRITSECT m_eventQueueCS; + /** Event for notifying this instance in case of a new + * event. */ + RTSEMEVENT m_eventQueueEvent; + /** Critical section for data access. */ + RTCRITSECT m_dataCS; + /** List of allowed formats. */ + RTCList<RTCString> m_lstAllowedFormats; + /** Number of failed attempts by the host + * to query for an active drag and drop operation on the guest. */ + uint16_t m_cFailedPendingAttempts; +}; + +/******************************************************************************* + * + * DragAndDropService Declaration + * + ******************************************************************************/ + +class DragAndDropService +{ +public: + DragAndDropService(void) + : m_pDisplay(NULL) + , m_hHGCMThread(NIL_RTTHREAD) + , m_hX11Thread(NIL_RTTHREAD) + , m_hEventSem(NIL_RTSEMEVENT) + , m_pCurDnD(NULL) + , m_fSrvStopping(false) + {} + + int init(void); + int run(bool fDaemonised = false); + void cleanup(void); + +private: + + static DECLCALLBACK(int) hgcmEventThread(RTTHREAD hThread, void *pvUser); + static DECLCALLBACK(int) x11EventThread(RTTHREAD hThread, void *pvUser); + + /* Private member vars */ + Display *m_pDisplay; + + /** Our (thread-safe) event queue with + * mixed events (DnD HGCM / X11). */ + RTCMTList<DnDEvent> m_eventQueue; + /** Critical section for providing serialized access to list + * event queue's contents. */ + RTCRITSECT m_eventQueueCS; + RTTHREAD m_hHGCMThread; + RTTHREAD m_hX11Thread; + RTSEMEVENT m_hEventSem; + DragInstance *m_pCurDnD; + bool m_fSrvStopping; + + friend class DragInstance; +}; + +/******************************************************************************* + * + * DragInstanc Implementation + * + ******************************************************************************/ + +DragInstance::DragInstance(Display *pDisplay, DragAndDropService *pParent) + : m_pParent(pParent) + , m_pDisplay(pDisplay) + , m_pScreen(0) + , m_wndRoot(0) + , m_wndCur(0) + , m_curVer(-1) + , m_pvSelReqData(NULL) + , m_cbSelReqData(0) + , m_enmMode(Unknown) + , m_enmState(Uninitialized) +{ +} + +/** + * Unitializes (destroys) this drag instance. + */ +void DragInstance::uninit(void) +{ + LogFlowFuncEnter(); + + if (m_wndProxy.hWnd != 0) + XDestroyWindow(m_pDisplay, m_wndProxy.hWnd); + + int rc2 = VbglR3DnDDisconnect(&m_dndCtx); + + if (m_pvSelReqData) + RTMemFree(m_pvSelReqData); + + rc2 = RTSemEventDestroy(m_eventQueueEvent); + AssertRC(rc2); + + rc2 = RTCritSectDelete(&m_eventQueueCS); + AssertRC(rc2); + + rc2 = RTCritSectDelete(&m_dataCS); + AssertRC(rc2); +} + +/** + * Resets this drag instance. + */ +void DragInstance::reset(void) +{ + LogFlowFuncEnter(); + + /* Hide the proxy win. */ + proxyWinHide(); + + int rc2 = RTCritSectEnter(&m_dataCS); + if (RT_SUCCESS(rc2)) + { + /* If we are currently the Xdnd selection owner, clear that. */ + Window pWnd = XGetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection)); + if (pWnd == m_wndProxy.hWnd) + XSetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection), None, CurrentTime); + + /* Clear any other DnD specific data on the proxy window. */ + wndXDnDClearFormatList(m_wndProxy.hWnd); + wndXDnDClearActionList(m_wndProxy.hWnd); + + /* Reset the internal state. */ + m_lstActions.clear(); + m_lstFormats.clear(); + m_wndCur = 0; + m_curVer = -1; + m_enmState = Initialized; + m_enmMode = Unknown; + m_eventQueueList.clear(); + m_cFailedPendingAttempts = 0; + + /* Reset the selection request buffer. */ + if (m_pvSelReqData) + { + RTMemFree(m_pvSelReqData); + m_pvSelReqData = NULL; + + Assert(m_cbSelReqData); + m_cbSelReqData = 0; + } + + RTCritSectLeave(&m_dataCS); + } +} + +/** + * Initializes this drag instance. + * + * @return IPRT status code. + * @param uScreenID X' screen ID to use. + */ +int DragInstance::init(uint32_t uScreenID) +{ + int rc = VbglR3DnDConnect(&m_dndCtx); + /* Note: Can return VINF_PERMISSION_DENIED if HGCM host service is not available. */ + if (rc != VINF_SUCCESS) + return rc; + + do + { + rc = RTSemEventCreate(&m_eventQueueEvent); + if (RT_FAILURE(rc)) + break; + + rc = RTCritSectInit(&m_eventQueueCS); + if (RT_FAILURE(rc)) + break; + + rc = RTCritSectInit(&m_dataCS); + if (RT_FAILURE(rc)) + break; + + /* + * Enough screens configured in the x11 server? + */ + if ((int)uScreenID > ScreenCount(m_pDisplay)) + { + rc = VERR_INVALID_PARAMETER; + break; + } +#if 0 + /* Get the screen number from the x11 server. */ + pDrag->screen = ScreenOfDisplay(m_pDisplay, uScreenID); + if (!pDrag->screen) + { + rc = VERR_GENERAL_FAILURE; + break; + } +#endif + m_screenID = uScreenID; + + /* Now query the corresponding root window of this screen. */ + m_wndRoot = RootWindow(m_pDisplay, m_screenID); + if (!m_wndRoot) + { + rc = VERR_GENERAL_FAILURE; + break; + } + + /* + * Create an invisible window which will act as proxy for the DnD + * operation. This window will be used for both the GH and HG + * direction. + */ + XSetWindowAttributes attr; + RT_ZERO(attr); + attr.event_mask = EnterWindowMask | LeaveWindowMask + | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; + attr.override_redirect = True; + attr.do_not_propagate_mask = NoEventMask; +#ifdef VBOX_DND_DEBUG_WND + attr.background_pixel = XWhitePixel(m_pDisplay, m_screenID); + attr.border_pixel = XBlackPixel(m_pDisplay, m_screenID); + m_wndProxy.hWnd = XCreateWindow(m_pDisplay, m_wndRoot /* Parent */, + 100, 100, /* Position */ + 100, 100, /* Width + height */ + 2, /* Border width */ + CopyFromParent, /* Depth */ + InputOutput, /* Class */ + CopyFromParent, /* Visual */ + CWBackPixel + | CWBorderPixel + | CWOverrideRedirect + | CWDontPropagate, /* Value mask */ + &attr); /* Attributes for value mask */ +#else + m_wndProxy.hWnd = XCreateWindow(m_pDisplay, m_wndRoot /* Parent */, + 0, 0, /* Position */ + 1, 1, /* Width + height */ + 0, /* Border width */ + CopyFromParent, /* Depth */ + InputOnly, /* Class */ + CopyFromParent, /* Visual */ + CWOverrideRedirect | CWDontPropagate, /* Value mask */ + &attr); /* Attributes for value mask */ +#endif + if (!m_wndProxy.hWnd) + { + LogRel(("DnD: Error creating proxy window\n")); + rc = VERR_GENERAL_FAILURE; + break; + } + + rc = m_wndProxy.init(m_pDisplay); + if (RT_FAILURE(rc)) + { + LogRel(("DnD: Error initializing proxy window, rc=%Rrc\n", rc)); + break; + } + +#ifdef VBOX_DND_DEBUG_WND + XFlush(m_pDisplay); + XMapWindow(m_pDisplay, m_wndProxy.hWnd); + XRaiseWindow(m_pDisplay, m_wndProxy.hWnd); + XFlush(m_pDisplay); +#endif + logInfo("Proxy window=%RU32, root window=%RU32 ...\n", m_wndProxy.hWnd, m_wndRoot); + + /* Set the window's name for easier lookup. */ + XStoreName(m_pDisplay, m_wndProxy.hWnd, "VBoxClientWndDnD"); + + /* Make the new window Xdnd aware. */ + Atom ver = VBOX_XDND_VERSION; + XChangeProperty(m_pDisplay, m_wndProxy.hWnd, xAtom(XA_XdndAware), XA_ATOM, 32, PropModeReplace, + reinterpret_cast<unsigned char*>(&ver), 1); + } while (0); + + if (RT_SUCCESS(rc)) + { + reset(); + } + else + logError("Initializing drag instance for screen %RU32 failed with rc=%Rrc\n", uScreenID, rc); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Logs an error message to the (release) logging instance. + * + * @param pszFormat Format string to log. + */ +VBOX_DND_FN_DECL_LOG(void) DragInstance::logError(const char *pszFormat, ...) +{ + va_list args; + va_start(args, pszFormat); + char *psz = NULL; + RTStrAPrintfV(&psz, pszFormat, args); + va_end(args); + + AssertPtr(psz); + LogFlowFunc(("%s", psz)); + LogRel(("DnD: %s", psz)); + + RTStrFree(psz); +} + +/** + * Logs an info message to the (release) logging instance. + * + * @param pszFormat Format string to log. + */ +VBOX_DND_FN_DECL_LOG(void) DragInstance::logInfo(const char *pszFormat, ...) +{ + va_list args; + va_start(args, pszFormat); + char *psz = NULL; + RTStrAPrintfV(&psz, pszFormat, args); + va_end(args); + + AssertPtr(psz); + LogFlowFunc(("%s", psz)); + LogRel2(("DnD: %s", psz)); + + RTStrFree(psz); +} + +/** + * Callback handler for a generic client message from a window. + * + * @return IPRT status code. + * @param e X11 event to handle. + */ +int DragInstance::onX11ClientMessage(const XEvent &e) +{ + AssertReturn(e.type == ClientMessage, VERR_INVALID_PARAMETER); + + LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState)); + LogFlowThisFunc(("Event wnd=%#x, msg=%s\n", e.xclient.window, xAtomToString(e.xclient.message_type).c_str())); + + int rc = VINF_SUCCESS; + + switch (m_enmMode) + { + case HG: + { + /* + * Client messages are used to inform us about the status of a XdndAware + * window, in response of some events we send to them. + */ + if ( e.xclient.message_type == xAtom(XA_XdndStatus) + && m_wndCur == static_cast<Window>(e.xclient.data.l[XdndStatusWindow])) + { + bool fAcceptDrop = ASMBitTest (&e.xclient.data.l[XdndStatusFlags], 0); /* Does the target accept the drop? */ + RTCString strActions = xAtomToString( e.xclient.data.l[XdndStatusAction]); +#ifdef LOG_ENABLED + bool fWantsPosition = ASMBitTest (&e.xclient.data.l[XdndStatusFlags], 1); /* Does the target want XdndPosition messages? */ + char *pszWndName = wndX11GetNameA(e.xclient.data.l[XdndStatusWindow]); + AssertPtr(pszWndName); + + /* + * The XdndStatus message tell us if the window will accept the DnD + * event and with which action. We immediately send this info down to + * the host as a response of a previous DnD message. + */ + LogFlowThisFunc(("XA_XdndStatus: wnd=%#x ('%s'), fAcceptDrop=%RTbool, fWantsPosition=%RTbool, strActions=%s\n", + e.xclient.data.l[XdndStatusWindow], pszWndName, fAcceptDrop, fWantsPosition, strActions.c_str())); + + RTStrFree(pszWndName); + + uint16_t x = RT_HI_U16((uint32_t)e.xclient.data.l[XdndStatusNoMsgXY]); + uint16_t y = RT_LO_U16((uint32_t)e.xclient.data.l[XdndStatusNoMsgXY]); + uint16_t cx = RT_HI_U16((uint32_t)e.xclient.data.l[XdndStatusNoMsgWH]); + uint16_t cy = RT_LO_U16((uint32_t)e.xclient.data.l[XdndStatusNoMsgWH]); + LogFlowThisFunc(("\tReported dead area: x=%RU16, y=%RU16, cx=%RU16, cy=%RU16\n", x, y, cx, cy)); +#endif + VBOXDNDACTION dndAction = VBOX_DND_ACTION_IGNORE; /* Default is ignoring. */ + /** @todo Compare this with the allowed actions. */ + if (fAcceptDrop) + dndAction = toHGCMAction(static_cast<Atom>(e.xclient.data.l[XdndStatusAction])); + + rc = VbglR3DnDHGSendAckOp(&m_dndCtx, dndAction); + } + else if (e.xclient.message_type == xAtom(XA_XdndFinished)) + { +#ifdef LOG_ENABLED + bool fSucceeded = ASMBitTest(&e.xclient.data.l[XdndFinishedFlags], 0); + + char *pszWndName = wndX11GetNameA(e.xclient.data.l[XdndFinishedWindow]); + AssertPtr(pszWndName); + + /* This message is sent on an un/successful DnD drop request. */ + LogFlowThisFunc(("XA_XdndFinished: wnd=%#x ('%s'), success=%RTbool, action=%s\n", + e.xclient.data.l[XdndFinishedWindow], pszWndName, fSucceeded, + xAtomToString(e.xclient.data.l[XdndFinishedAction]).c_str())); + + RTStrFree(pszWndName); +#endif + + reset(); + } + else + { + char *pszWndName = wndX11GetNameA(e.xclient.data.l[0]); + AssertPtr(pszWndName); + LogFlowThisFunc(("Unhandled: wnd=%#x ('%s'), msg=%s\n", + e.xclient.data.l[0], pszWndName, xAtomToString(e.xclient.message_type).c_str())); + RTStrFree(pszWndName); + + rc = VERR_NOT_SUPPORTED; + } + + break; + } + + case Unknown: /* Mode not set (yet). */ + case GH: + { + /* + * This message marks the beginning of a new drag and drop + * operation on the guest. + */ + if (e.xclient.message_type == xAtom(XA_XdndEnter)) + { + LogFlowFunc(("XA_XdndEnter\n")); + + /* + * Get the window which currently has the XA_XdndSelection + * bit set. + */ + Window wndSelection = XGetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection)); + + char *pszWndName = wndX11GetNameA(wndSelection); + AssertPtr(pszWndName); + LogFlowThisFunc(("wndSelection=%RU32 ('%s'), wndProxy=%RU32\n", wndSelection, pszWndName, m_wndProxy.hWnd)); + RTStrFree(pszWndName); + + mouseButtonSet(m_wndProxy.hWnd, -1, -1, 1, true /* fPress */); + + /* + * Update our state and the window handle to process. + */ + int rc2 = RTCritSectEnter(&m_dataCS); + if (RT_SUCCESS(rc2)) + { + m_wndCur = wndSelection; + m_curVer = e.xclient.data.l[XdndEnterFlags] >> XdndEnterVersionRShift; + Assert(m_wndCur == (Window)e.xclient.data.l[XdndEnterWindow]); /* Source window. */ +#ifdef DEBUG + XWindowAttributes xwa; + XGetWindowAttributes(m_pDisplay, m_wndCur, &xwa); + LogFlowThisFunc(("wndCur=%#x, x=%d, y=%d, width=%d, height=%d\n", m_wndCur, xwa.x, xwa.y, xwa.width, xwa.height)); +#endif + /* + * Retrieve supported formats. + */ + + /* Check if the MIME types are in the message itself or if we need + * to fetch the XdndTypeList property from the window. */ + bool fMoreTypes = e.xclient.data.l[XdndEnterFlags] & XdndEnterMoreTypesFlag; + LogFlowThisFunc(("XdndVer=%d, fMoreTypes=%RTbool\n", m_curVer, fMoreTypes)); + if (!fMoreTypes) + { + /* Only up to 3 format types supported. */ + /* Start with index 2 (first item). */ + for (int i = 2; i < 5; i++) + { + LogFlowThisFunc(("\t%s\n", gX11->xAtomToString(e.xclient.data.l[i]).c_str())); + m_lstFormats.append(e.xclient.data.l[i]); + } + } + else + { + /* More than 3 format types supported. */ + rc = wndXDnDGetFormatList(wndSelection, m_lstFormats); + } + + /* + * Retrieve supported actions. + */ + if (RT_SUCCESS(rc)) + { + if (m_curVer >= 2) /* More than one action allowed since protocol version 2. */ + { + rc = wndXDnDGetActionList(wndSelection, m_lstActions); + } + else /* Only "copy" action allowed on legacy applications. */ + m_lstActions.append(XA_XdndActionCopy); + } + + if (RT_SUCCESS(rc)) + { + m_enmMode = GH; + m_enmState = Dragging; + } + + RTCritSectLeave(&m_dataCS); + } + } + else if ( e.xclient.message_type == xAtom(XA_XdndPosition) + && m_wndCur == static_cast<Window>(e.xclient.data.l[XdndPositionWindow])) + { + if (m_enmState != Dragging) /* Wrong mode? Bail out. */ + { + reset(); + break; + } +#ifdef LOG_ENABLED + int32_t iPos = e.xclient.data.l[XdndPositionXY]; + Atom atmAction = m_curVer >= 2 /* Actions other than "copy" or only supported since protocol version 2. */ + ? e.xclient.data.l[XdndPositionAction] : xAtom(XA_XdndActionCopy); + LogFlowThisFunc(("XA_XdndPosition: wndProxy=%RU32, wndCur=%RU32, x=%RI32, y=%RI32, strAction=%s\n", + m_wndProxy.hWnd, m_wndCur, RT_HIWORD(iPos), RT_LOWORD(iPos), + xAtomToString(atmAction).c_str())); +#endif + + bool fAcceptDrop = true; + + /* Reply with a XdndStatus message to tell the source whether + * the data can be dropped or not. */ + XClientMessageEvent m; + RT_ZERO(m); + m.type = ClientMessage; + m.display = m_pDisplay; + m.window = e.xclient.data.l[XdndPositionWindow]; + m.message_type = xAtom(XA_XdndStatus); + m.format = 32; + m.data.l[XdndStatusWindow] = m_wndProxy.hWnd; + m.data.l[XdndStatusFlags] = fAcceptDrop ? RT_BIT(0) : 0; /* Whether to accept the drop or not. */ + + /* We don't want any new XA_XdndPosition messages while being + * in our proxy window. */ + m.data.l[XdndStatusNoMsgXY] = RT_MAKE_U32(m_wndProxy.iY, m_wndProxy.iX); + m.data.l[XdndStatusNoMsgWH] = RT_MAKE_U32(m_wndProxy.iHeight, m_wndProxy.iWidth); + + /** @todo Handle default action! */ + m.data.l[XdndStatusAction] = fAcceptDrop ? toAtomAction(VBOX_DND_ACTION_COPY) : None; + + int xRc = XSendEvent(m_pDisplay, e.xclient.data.l[XdndPositionWindow], + False /* Propagate */, NoEventMask, reinterpret_cast<XEvent *>(&m)); + if (xRc == 0) + logError("Error sending position XA_XdndStatus event to current window=%#x: %s\n", + m_wndCur, gX11->xErrorToString(xRc).c_str()); + } + else if ( e.xclient.message_type == xAtom(XA_XdndLeave) + && m_wndCur == static_cast<Window>(e.xclient.data.l[XdndLeaveWindow])) + { + LogFlowThisFunc(("XA_XdndLeave\n")); + logInfo("Guest to host transfer canceled by the guest source window\n"); + + /* Start over. */ + reset(); + } + else if ( e.xclient.message_type == xAtom(XA_XdndDrop) + && m_wndCur == static_cast<Window>(e.xclient.data.l[XdndDropWindow])) + { + LogFlowThisFunc(("XA_XdndDrop\n")); + + if (m_enmState != Dropped) /* Wrong mode? Bail out. */ + { + /* Can occur when dragging from guest->host, but then back in to the guest again. */ + logInfo("Could not drop on own proxy window\n"); /* Not fatal. */ + + /* Let the source know. */ + rc = m_wndProxy.sendFinished(m_wndCur, VBOX_DND_ACTION_IGNORE); + + /* Start over. */ + reset(); + break; + } + + m_eventQueueList.append(e); + rc = RTSemEventSignal(m_eventQueueEvent); + } + else /* Unhandled event, abort. */ + { + logInfo("Unhandled event from wnd=%#x, msg=%s\n", e.xclient.window, xAtomToString(e.xclient.message_type).c_str()); + + /* Let the source know. */ + rc = m_wndProxy.sendFinished(m_wndCur, VBOX_DND_ACTION_IGNORE); + + /* Start over. */ + reset(); + } + break; + } + + default: + { + AssertMsgFailed(("Drag and drop mode not implemented: %RU32\n", m_enmMode)); + rc = VERR_NOT_IMPLEMENTED; + break; + } + } + + LogFlowThisFunc(("Returning rc=%Rrc\n", rc)); + return rc; +} + +int DragInstance::onX11MotionNotify(const XEvent &e) +{ + RT_NOREF1(e); + LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState)); + + return VINF_SUCCESS; +} + +/** + * Callback handler for being notified if some other window now + * is the owner of the current selection. + * + * @return IPRT status code. + * @param e X11 event to handle. + * + * @remark + */ +int DragInstance::onX11SelectionClear(const XEvent &e) +{ + RT_NOREF1(e); + LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState)); + + return VINF_SUCCESS; +} + +/** + * Callback handler for a XDnD selection notify from a window. This is needed + * to let the us know if a certain window has drag'n drop data to share with us, + * e.g. our proxy window. + * + * @return IPRT status code. + * @param e X11 event to handle. + */ +int DragInstance::onX11SelectionNotify(const XEvent &e) +{ + AssertReturn(e.type == SelectionNotify, VERR_INVALID_PARAMETER); + + LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState)); + + int rc; + + switch (m_enmMode) + { + case GH: + { + if (m_enmState == Dropped) + { + m_eventQueueList.append(e); + rc = RTSemEventSignal(m_eventQueueEvent); + } + else + rc = VERR_WRONG_ORDER; + break; + } + + default: + { + LogFlowThisFunc(("Unhandled: wnd=%#x, msg=%s\n", + e.xclient.data.l[0], xAtomToString(e.xclient.message_type).c_str())); + rc = VERR_INVALID_STATE; + break; + } + } + + LogFlowThisFunc(("Returning rc=%Rrc\n", rc)); + return rc; +} + +/** + * Callback handler for a XDnD selection request from a window. This is needed + * to retrieve the data required to complete the actual drag'n drop operation. + * + * @returns IPRT status code. + * @param e X11 event to handle. + */ +int DragInstance::onX11SelectionRequest(const XEvent &e) +{ + AssertReturn(e.type == SelectionRequest, VERR_INVALID_PARAMETER); + + LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState)); + LogFlowThisFunc(("Event owner=%#x, requestor=%#x, selection=%s, target=%s, prop=%s, time=%u\n", + e.xselectionrequest.owner, + e.xselectionrequest.requestor, + xAtomToString(e.xselectionrequest.selection).c_str(), + xAtomToString(e.xselectionrequest.target).c_str(), + xAtomToString(e.xselectionrequest.property).c_str(), + e.xselectionrequest.time)); + int rc; + + switch (m_enmMode) + { + case HG: + { + rc = VINF_SUCCESS; + + char *pszWndName = wndX11GetNameA(e.xselectionrequest.requestor); + AssertPtr(pszWndName); + + /* + * Start by creating a refusal selection notify message. + * That way we only need to care for the success case. + */ + + XEvent s; + RT_ZERO(s); + s.xselection.type = SelectionNotify; + s.xselection.display = e.xselectionrequest.display; + s.xselection.requestor = e.xselectionrequest.requestor; + s.xselection.selection = e.xselectionrequest.selection; + s.xselection.target = e.xselectionrequest.target; + s.xselection.property = None; /* "None" means refusal. */ + s.xselection.time = e.xselectionrequest.time; + + const XSelectionRequestEvent *pReq = &e.xselectionrequest; + +#ifdef DEBUG + LogFlowFunc(("Supported formats:\n")); + for (size_t i = 0; i < m_lstFormats.size(); i++) + LogFlowFunc(("\t%s\n", xAtomToString(m_lstFormats.at(i)).c_str())); +#endif + /* Is the requestor asking for the possible MIME types? */ + if (pReq->target == xAtom(XA_TARGETS)) + { + logInfo("Target window %#x ('%s') asking for target list\n", e.xselectionrequest.requestor, pszWndName); + + /* If so, set the window property with the formats on the requestor + * window. */ + rc = wndXDnDSetFormatList(pReq->requestor, pReq->property, m_lstFormats); + if (RT_SUCCESS(rc)) + s.xselection.property = pReq->property; + } + /* Is the requestor asking for a specific MIME type (we support)? */ + else if (m_lstFormats.contains(pReq->target)) + { + logInfo("Target window %#x ('%s') is asking for data as '%s'\n", + pReq->requestor, pszWndName, xAtomToString(pReq->target).c_str()); + + /* Did we not drop our stuff to the guest yet? Bail out. */ + if (m_enmState != Dropped) + { + LogFlowThisFunc(("Wrong state (%RU32), refusing request\n", m_enmState)); + } + /* Did we not store the requestor's initial selection request yet? Then do so now. */ + else + { + /* Get the data format the requestor wants from us. */ + RTCString strFormat = xAtomToString(pReq->target); + Assert(strFormat.isNotEmpty()); + logInfo("Target window=%#x requested data from host as '%s', rc=%Rrc\n", + pReq->requestor, strFormat.c_str(), rc); + + /* Make a copy of the MIME data to be passed back. The X server will be become + * the new owner of that data, so no deletion needed. */ + /** @todo Do we need to do some more conversion here? XConvertSelection? */ + void *pvData = RTMemDup(m_pvSelReqData, m_cbSelReqData); + uint32_t cbData = m_cbSelReqData; + + /* Always return the requested property. */ + s.xselection.property = pReq->property; + + /* Note: Always seems to return BadRequest. Seems fine. */ + int xRc = XChangeProperty(s.xselection.display, s.xselection.requestor, s.xselection.property, + s.xselection.target, 8, PropModeReplace, + reinterpret_cast<const unsigned char*>(pvData), cbData); + + LogFlowFunc(("Changing property '%s' (target '%s') of window=%RU32: %s\n", + xAtomToString(pReq->property).c_str(), + xAtomToString(pReq->target).c_str(), + pReq->requestor, + gX11->xErrorToString(xRc).c_str())); + NOREF(xRc); + } + } + /* Anything else. */ + else + { + logError("Refusing unknown command/format '%s' of wnd=%#x ('%s')\n", + xAtomToString(e.xselectionrequest.target).c_str(), pReq->requestor, pszWndName); + rc = VERR_NOT_SUPPORTED; + } + + LogFlowThisFunc(("Offering type '%s', property '%s' to wnd=%#x ...\n", + xAtomToString(pReq->target).c_str(), + xAtomToString(pReq->property).c_str(), pReq->requestor)); + + int xRc = XSendEvent(pReq->display, pReq->requestor, True /* Propagate */, 0, &s); + if (xRc == 0) + logError("Error sending SelectionNotify(1) event to wnd=%#x: %s\n", pReq->requestor, + gX11->xErrorToString(xRc).c_str()); + XFlush(pReq->display); + + if (pszWndName) + RTStrFree(pszWndName); + break; + } + + default: + rc = VERR_INVALID_STATE; + break; + } + + LogFlowThisFunc(("Returning rc=%Rrc\n", rc)); + return rc; +} + +/** + * Handles X11 events, called by x11EventThread. + * + * @returns IPRT status code. + * @param e X11 event to handle. + */ +int DragInstance::onX11Event(const XEvent &e) +{ + int rc; + + LogFlowThisFunc(("X11 event, type=%d\n", e.type)); + switch (e.type) + { + /* + * This can happen if a guest->host drag operation + * goes back from the host to the guest. This is not what + * we want and thus resetting everything. + */ + case ButtonPress: + case ButtonRelease: + LogFlowThisFunc(("Mouse button press/release\n")); + rc = VINF_SUCCESS; + + reset(); + break; + + case ClientMessage: + rc = onX11ClientMessage(e); + break; + + case SelectionClear: + rc = onX11SelectionClear(e); + break; + + case SelectionNotify: + rc = onX11SelectionNotify(e); + break; + + case SelectionRequest: + rc = onX11SelectionRequest(e); + break; + + case MotionNotify: + rc = onX11MotionNotify(e); + break; + + default: + rc = VERR_NOT_IMPLEMENTED; + break; + } + + LogFlowThisFunc(("rc=%Rrc\n", rc)); + return rc; +} + +int DragInstance::waitForStatusChange(uint32_t enmState, RTMSINTERVAL uTimeoutMS /* = 30000 */) +{ + const uint64_t uiStart = RTTimeMilliTS(); + volatile uint32_t enmCurState; + + int rc = VERR_TIMEOUT; + + LogFlowFunc(("enmState=%RU32, uTimeoutMS=%RU32\n", enmState, uTimeoutMS)); + + do + { + enmCurState = ASMAtomicReadU32(&m_enmState); + if (enmCurState == enmState) + { + rc = VINF_SUCCESS; + break; + } + } + while (RTTimeMilliTS() - uiStart < uTimeoutMS); + + LogFlowThisFunc(("Returning %Rrc\n", rc)); + return rc; +} + +#ifdef VBOX_WITH_DRAG_AND_DROP_GH +/** + * Waits for an X11 event of a specific type. + * + * @returns IPRT status code. + * @param evX Reference where to store the event into. + * @param iType Event type to wait for. + * @param uTimeoutMS Timeout (in ms) to wait for the event. + */ +bool DragInstance::waitForX11Msg(XEvent &evX, int iType, RTMSINTERVAL uTimeoutMS /* = 100 */) +{ + LogFlowThisFunc(("iType=%d, uTimeoutMS=%RU32, cEventQueue=%zu\n", iType, uTimeoutMS, m_eventQueueList.size())); + + bool fFound = false; + const uint64_t uiStart = RTTimeMilliTS(); + + do + { + /* Check if there is a client message in the queue. */ + for (size_t i = 0; i < m_eventQueueList.size(); i++) + { + int rc2 = RTCritSectEnter(&m_eventQueueCS); + if (RT_SUCCESS(rc2)) + { + XEvent e = m_eventQueueList.at(i); + + fFound = e.type == iType; + if (fFound) + { + m_eventQueueList.removeAt(i); + evX = e; + } + + rc2 = RTCritSectLeave(&m_eventQueueCS); + AssertRC(rc2); + + if (fFound) + break; + } + } + + if (fFound) + break; + + int rc2 = RTSemEventWait(m_eventQueueEvent, 25 /* ms */); + if ( RT_FAILURE(rc2) + && rc2 != VERR_TIMEOUT) + { + LogFlowFunc(("Waiting failed with rc=%Rrc\n", rc2)); + break; + } + } + while (RTTimeMilliTS() - uiStart < uTimeoutMS); + + LogFlowThisFunc(("Returning fFound=%RTbool, msRuntime=%RU64\n", fFound, RTTimeMilliTS() - uiStart)); + return fFound; +} + +/** + * Waits for an X11 client message of a specific type. + * + * @returns IPRT status code. + * @param evMsg Reference where to store the event into. + * @param aType Event type to wait for. + * @param uTimeoutMS Timeout (in ms) to wait for the event. + */ +bool DragInstance::waitForX11ClientMsg(XClientMessageEvent &evMsg, Atom aType, + RTMSINTERVAL uTimeoutMS /* = 100 */) +{ + LogFlowThisFunc(("aType=%s, uTimeoutMS=%RU32, cEventQueue=%zu\n", + xAtomToString(aType).c_str(), uTimeoutMS, m_eventQueueList.size())); + + bool fFound = false; + const uint64_t uiStart = RTTimeMilliTS(); + do + { + /* Check if there is a client message in the queue. */ + for (size_t i = 0; i < m_eventQueueList.size(); i++) + { + int rc2 = RTCritSectEnter(&m_eventQueueCS); + if (RT_SUCCESS(rc2)) + { + XEvent e = m_eventQueueList.at(i); + if ( e.type == ClientMessage + && e.xclient.message_type == aType) + { + m_eventQueueList.removeAt(i); + evMsg = e.xclient; + + fFound = true; + } + + if (e.type == ClientMessage) + { + LogFlowThisFunc(("Client message: Type=%ld (%s)\n", + e.xclient.message_type, xAtomToString(e.xclient.message_type).c_str())); + } + else + LogFlowThisFunc(("X message: Type=%d\n", e.type)); + + rc2 = RTCritSectLeave(&m_eventQueueCS); + AssertRC(rc2); + + if (fFound) + break; + } + } + + if (fFound) + break; + + int rc2 = RTSemEventWait(m_eventQueueEvent, 25 /* ms */); + if ( RT_FAILURE(rc2) + && rc2 != VERR_TIMEOUT) + { + LogFlowFunc(("Waiting failed with rc=%Rrc\n", rc2)); + break; + } + } + while (RTTimeMilliTS() - uiStart < uTimeoutMS); + + LogFlowThisFunc(("Returning fFound=%RTbool, msRuntime=%RU64\n", fFound, RTTimeMilliTS() - uiStart)); + return fFound; +} +#endif /* VBOX_WITH_DRAG_AND_DROP_GH */ + +/* + * Host -> Guest + */ + +/** + * Host -> Guest: Event signalling that the host's (mouse) cursor just entered the VM's (guest's) display + * area. + * + * @returns IPRT status code. + * @param lstFormats List of supported formats from the host. + * @param dndListActionsAllowed (ORed) List of supported actions from the host. + */ +int DragInstance::hgEnter(const RTCList<RTCString> &lstFormats, uint32_t dndListActionsAllowed) +{ + LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState)); + + if (m_enmMode != Unknown) + return VERR_INVALID_STATE; + + reset(); + +#ifdef DEBUG + LogFlowThisFunc(("dndListActionsAllowed=0x%x, lstFormats=%zu: ", dndListActionsAllowed, lstFormats.size())); + for (size_t i = 0; i < lstFormats.size(); ++i) + LogFlow(("'%s' ", lstFormats.at(i).c_str())); + LogFlow(("\n")); +#endif + + int rc; + + do + { + /* Check if the VM session has changed and reconnect to the HGCM service if necessary. */ + rc = checkForSessionChange(); + if (RT_FAILURE(rc)) + break; + + rc = toAtomList(lstFormats, m_lstFormats); + if (RT_FAILURE(rc)) + break; + + /* If we have more than 3 formats we have to use the type list extension. */ + if (m_lstFormats.size() > 3) + { + rc = wndXDnDSetFormatList(m_wndProxy.hWnd, xAtom(XA_XdndTypeList), m_lstFormats); + if (RT_FAILURE(rc)) + break; + } + + /* Announce the possible actions. */ + VBoxDnDAtomList lstActions; + rc = toAtomActions(dndListActionsAllowed, lstActions); + if (RT_FAILURE(rc)) + break; + rc = wndXDnDSetActionList(m_wndProxy.hWnd, lstActions); + + /* Set the DnD selection owner to our window. */ + /** @todo Don't use CurrentTime -- according to ICCCM section 2.1. */ + XSetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection), m_wndProxy.hWnd, CurrentTime); + + m_enmMode = HG; + m_enmState = Dragging; + + } while (0); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Host -> Guest: Event signalling that the host's (mouse) cursor has left the VM's (guest's) + * display area. + */ +int DragInstance::hgLeave(void) +{ + if (m_enmMode == HG) /* Only reset if in the right operation mode. */ + reset(); + + return VINF_SUCCESS; +} + +/** + * Host -> Guest: Event signalling that the host's (mouse) cursor has been moved within the VM's + * (guest's) display area. + * + * @returns IPRT status code. + * @param uPosX Relative X position within the guest's display area. + * @param uPosY Relative Y position within the guest's display area. + * @param dndActionDefault Default action the host wants to perform on the guest + * as soon as the operation successfully finishes. + */ +int DragInstance::hgMove(uint32_t uPosX, uint32_t uPosY, VBOXDNDACTION dndActionDefault) +{ + LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState)); + LogFlowThisFunc(("uPosX=%RU32, uPosY=%RU32, dndActionDefault=0x%x\n", uPosX, uPosY, dndActionDefault)); + + if ( m_enmMode != HG + || m_enmState != Dragging) + { + return VERR_INVALID_STATE; + } + + int rc = VINF_SUCCESS; + int xRc = Success; + + /* Move the mouse cursor within the guest. */ + mouseCursorMove(uPosX, uPosY); + + long newVer = -1; /* This means the current window is _not_ XdndAware. */ + + /* Search for the application window below the cursor. */ + Window wndCursor = gX11->applicationWindowBelowCursor(m_wndRoot); + if (wndCursor != None) + { + /* Temp stuff for the XGetWindowProperty call. */ + Atom atmp; + int fmt; + unsigned long cItems, cbRemaining; + unsigned char *pcData = NULL; + + /* Query the XdndAware property from the window. We are interested in + * the version and if it is XdndAware at all. */ + xRc = XGetWindowProperty(m_pDisplay, wndCursor, xAtom(XA_XdndAware), + 0, 2, False, AnyPropertyType, + &atmp, &fmt, &cItems, &cbRemaining, &pcData); + if (xRc != Success) + { + logError("Error getting properties of cursor window=%#x: %s\n", wndCursor, gX11->xErrorToString(xRc).c_str()); + } + else + { + if (pcData == NULL || fmt != 32 || cItems != 1) + { + /** @todo Do we need to deal with this? */ + logError("Wrong window properties for window %#x: pcData=%#x, iFmt=%d, cItems=%ul\n", + wndCursor, pcData, fmt, cItems); + } + else + { + /* Get the current window's Xdnd version. */ + newVer = reinterpret_cast<long *>(pcData)[0]; + } + + XFree(pcData); + } + } + +#ifdef DEBUG + char *pszNameCursor = wndX11GetNameA(wndCursor); + AssertPtr(pszNameCursor); + char *pszNameCur = wndX11GetNameA(m_wndCur); + AssertPtr(pszNameCur); + + LogFlowThisFunc(("wndCursor=%x ('%s', Xdnd version %ld), wndCur=%x ('%s', Xdnd version %ld)\n", + wndCursor, pszNameCursor, newVer, m_wndCur, pszNameCur, m_curVer)); + + RTStrFree(pszNameCursor); + RTStrFree(pszNameCur); +#endif + + if ( wndCursor != m_wndCur + && m_curVer != -1) + { + LogFlowThisFunc(("XA_XdndLeave: window=%#x\n", m_wndCur)); + + char *pszWndName = wndX11GetNameA(m_wndCur); + AssertPtr(pszWndName); + logInfo("Left old window %#x ('%s'), Xdnd version=%ld\n", m_wndCur, pszWndName, newVer); + RTStrFree(pszWndName); + + /* We left the current XdndAware window. Announce this to the current indow. */ + XClientMessageEvent m; + RT_ZERO(m); + m.type = ClientMessage; + m.display = m_pDisplay; + m.window = m_wndCur; + m.message_type = xAtom(XA_XdndLeave); + m.format = 32; + m.data.l[XdndLeaveWindow] = m_wndProxy.hWnd; + + xRc = XSendEvent(m_pDisplay, m_wndCur, False, NoEventMask, reinterpret_cast<XEvent*>(&m)); + if (xRc == 0) + logError("Error sending XA_XdndLeave event to old window=%#x: %s\n", m_wndCur, gX11->xErrorToString(xRc).c_str()); + + /* Reset our current window. */ + m_wndCur = 0; + m_curVer = -1; + } + + /* + * Do we have a new Xdnd-aware window which now is under the cursor? + */ + if ( wndCursor != m_wndCur + && newVer != -1) + { + LogFlowThisFunc(("XA_XdndEnter: window=%#x\n", wndCursor)); + + char *pszWndName = wndX11GetNameA(wndCursor); + AssertPtr(pszWndName); + logInfo("Entered new window %#x ('%s'), supports Xdnd version=%ld\n", wndCursor, pszWndName, newVer); + RTStrFree(pszWndName); + + /* + * We enter a new window. Announce the XdndEnter event to the new + * window. The first three mime types are attached to the event (the + * others could be requested by the XdndTypeList property from the + * window itself). + */ + XClientMessageEvent m; + RT_ZERO(m); + m.type = ClientMessage; + m.display = m_pDisplay; + m.window = wndCursor; + m.message_type = xAtom(XA_XdndEnter); + m.format = 32; + m.data.l[XdndEnterWindow] = m_wndProxy.hWnd; + m.data.l[XdndEnterFlags] = RT_MAKE_U32_FROM_U8( + /* Bit 0 is set if the source supports more than three data types. */ + m_lstFormats.size() > 3 ? RT_BIT(0) : 0, + /* Reserved for future use. */ + 0, 0, + /* Protocol version to use. */ + RT_MIN(VBOX_XDND_VERSION, newVer)); + m.data.l[XdndEnterType1] = m_lstFormats.value(0, None); /* First data type to use. */ + m.data.l[XdndEnterType2] = m_lstFormats.value(1, None); /* Second data type to use. */ + m.data.l[XdndEnterType3] = m_lstFormats.value(2, None); /* Third data type to use. */ + + xRc = XSendEvent(m_pDisplay, wndCursor, False, NoEventMask, reinterpret_cast<XEvent*>(&m)); + if (xRc == 0) + logError("Error sending XA_XdndEnter event to window=%#x: %s\n", wndCursor, gX11->xErrorToString(xRc).c_str()); + } + + if (newVer != -1) + { + Assert(wndCursor != None); + + LogFlowThisFunc(("XA_XdndPosition: xPos=%RU32, yPos=%RU32 to window=%#x\n", uPosX, uPosY, wndCursor)); + + /* + * Send a XdndPosition event with the proposed action to the guest. + */ + Atom pa = toAtomAction(dndActionDefault); + LogFlowThisFunc(("strAction=%s\n", xAtomToString(pa).c_str())); + + XClientMessageEvent m; + RT_ZERO(m); + m.type = ClientMessage; + m.display = m_pDisplay; + m.window = wndCursor; + m.message_type = xAtom(XA_XdndPosition); + m.format = 32; + m.data.l[XdndPositionWindow] = m_wndProxy.hWnd; /* X window ID of source window. */ + m.data.l[XdndPositionXY] = RT_MAKE_U32(uPosY, uPosX); /* Cursor coordinates relative to the root window. */ + m.data.l[XdndPositionTimeStamp] = CurrentTime; /* Timestamp for retrieving data. */ + m.data.l[XdndPositionAction] = pa; /* Actions requested by the user. */ + + xRc = XSendEvent(m_pDisplay, wndCursor, False, NoEventMask, reinterpret_cast<XEvent*>(&m)); + if (xRc == 0) + logError("Error sending XA_XdndPosition event to current window=%#x: %s\n", wndCursor, gX11->xErrorToString(xRc).c_str()); + } + + if (newVer == -1) + { + /* No window to process, so send a ignore ack event to the host. */ + rc = VbglR3DnDHGSendAckOp(&m_dndCtx, VBOX_DND_ACTION_IGNORE); + } + else + { + Assert(wndCursor != None); + + m_wndCur = wndCursor; + m_curVer = newVer; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Host -> Guest: Event signalling that the host has dropped the data over the VM (guest) window. + * + * @returns IPRT status code. + * @param uPosX Relative X position within the guest's display area. + * @param uPosY Relative Y position within the guest's display area. + * @param dndActionDefault Default action the host wants to perform on the guest + * as soon as the operation successfully finishes. + */ +int DragInstance::hgDrop(uint32_t uPosX, uint32_t uPosY, VBOXDNDACTION dndActionDefault) +{ + RT_NOREF3(uPosX, uPosY, dndActionDefault); + LogFlowThisFunc(("wndCur=%RU32, wndProxy=%RU32, mode=%RU32, state=%RU32\n", m_wndCur, m_wndProxy.hWnd, m_enmMode, m_enmState)); + LogFlowThisFunc(("uPosX=%RU32, uPosY=%RU32, dndActionDefault=0x%x\n", uPosX, uPosY, dndActionDefault)); + + if ( m_enmMode != HG + || m_enmState != Dragging) + { + return VERR_INVALID_STATE; + } + + /* Set the state accordingly. */ + m_enmState = Dropped; + + /* + * Ask the host to send the raw data, as we don't (yet) know which format + * the guest exactly expects. As blocking in a SelectionRequest message turned + * out to be very unreliable (e.g. with KDE apps) we request to start transferring + * file/directory data (if any) here. + */ + char szFormat[] = { "text/uri-list" }; + + int rc = VbglR3DnDHGSendReqData(&m_dndCtx, szFormat); + logInfo("Drop event from host resulted in: %Rrc\n", rc); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Host -> Guest: Event signalling that the host has finished sending drag'n drop + * data to the guest for further processing. + * + * @returns IPRT status code. + * @param pMetaData Pointer to meta data from host. + */ +int DragInstance::hgDataReceive(PVBGLR3GUESTDNDMETADATA pMetaData) +{ + LogFlowThisFunc(("enmMode=%RU32, enmState=%RU32\n", m_enmMode, m_enmState)); + LogFlowThisFunc(("enmMetaDataType=%RU32\n", pMetaData->enmType)); + + if ( m_enmMode != HG + || m_enmState != Dropped) + { + return VERR_INVALID_STATE; + } + + if ( pMetaData->pvMeta == NULL + || pMetaData->cbMeta == 0) + { + return VERR_INVALID_PARAMETER; + } + + int rc = VINF_SUCCESS; + + const void *pvData = pMetaData->pvMeta; + const uint32_t cbData = pMetaData->cbMeta; + + /* + * At this point all data needed (including sent files/directories) should + * be on the guest, so proceed working on communicating with the target window. + */ + logInfo("Received %RU32 bytes of URI list meta data from host\n", cbData); + + /* Destroy any old data. */ + if (m_pvSelReqData) + { + Assert(m_cbSelReqData); + + RTMemFree(m_pvSelReqData); /** @todo RTMemRealloc? */ + m_cbSelReqData = 0; + } + + /** @todo Handle incremental transfers. */ + + /* Make a copy of the data. This data later then will be used to fill into + * the selection request. */ + if (cbData) + { + m_pvSelReqData = RTMemAlloc(cbData); + if (!m_pvSelReqData) + return VERR_NO_MEMORY; + + memcpy(m_pvSelReqData, pvData, cbData); + m_cbSelReqData = cbData; + } + + /* + * Send a drop event to the current window (target). + * This window in turn then will raise a SelectionRequest message to our proxy window, + * which we will handle in our onX11SelectionRequest handler. + * + * The SelectionRequest will tell us in which format the target wants the data from the host. + */ + XClientMessageEvent m; + RT_ZERO(m); + m.type = ClientMessage; + m.display = m_pDisplay; + m.window = m_wndCur; + m.message_type = xAtom(XA_XdndDrop); + m.format = 32; + m.data.l[XdndDropWindow] = m_wndProxy.hWnd; /* Source window. */ + m.data.l[XdndDropFlags] = 0; /* Reserved for future use. */ + m.data.l[XdndDropTimeStamp] = CurrentTime; /* Our DnD data does not rely on any timing, so just use the current time. */ + + int xRc = XSendEvent(m_pDisplay, m_wndCur, False /* Propagate */, NoEventMask, reinterpret_cast<XEvent*>(&m)); + if (xRc == 0) + logError("Error sending XA_XdndDrop event to window=%#x: %s\n", m_wndCur, gX11->xErrorToString(xRc).c_str()); + XFlush(m_pDisplay); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Checks if the VM session has changed (can happen when restoring the VM from a saved state) + * and do a reconnect to the DnD HGCM service. + * + * @returns IPRT status code. + */ +int DragInstance::checkForSessionChange(void) +{ + uint64_t uSessionID; + int rc = VbglR3GetSessionId(&uSessionID); + if ( RT_SUCCESS(rc) + && uSessionID != m_dndCtx.uSessionID) + { + LogFlowThisFunc(("VM session has changed to %RU64\n", uSessionID)); + + rc = VbglR3DnDDisconnect(&m_dndCtx); + AssertRC(rc); + + rc = VbglR3DnDConnect(&m_dndCtx); + AssertRC(rc); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +#ifdef VBOX_WITH_DRAG_AND_DROP_GH +/** + * Guest -> Host: Event signalling that the host is asking whether there is a pending + * drag event on the guest (to the host). + * + * @returns IPRT status code. + */ +int DragInstance::ghIsDnDPending(void) +{ + LogFlowThisFunc(("mode=%RU32, state=%RU32\n", m_enmMode, m_enmState)); + + int rc; + + RTCString strFormats = "\r\n"; /** @todo If empty, IOCTL fails with VERR_ACCESS_DENIED. */ + VBOXDNDACTION dndActionDefault = VBOX_DND_ACTION_IGNORE; + VBOXDNDACTIONLIST dndActionList = VBOX_DND_ACTION_IGNORE; + + /* Currently in wrong mode? Bail out. */ + if (m_enmMode == HG) + { + rc = VERR_INVALID_STATE; + } + /* Message already processed successfully? */ + else if ( m_enmMode == GH + && ( m_enmState == Dragging + || m_enmState == Dropped) + ) + { + /* No need to query for the source window again. */ + rc = VINF_SUCCESS; + } + else + { + /* Check if the VM session has changed and reconnect to the HGCM service if necessary. */ + rc = checkForSessionChange(); + + /* Determine the current window which currently has the XdndSelection set. */ + Window wndSelection = XGetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection)); + LogFlowThisFunc(("wndSelection=%#x, wndProxy=%#x, wndCur=%#x\n", wndSelection, m_wndProxy.hWnd, m_wndCur)); + + /* Is this another window which has a Xdnd selection and not our proxy window? */ + if ( RT_SUCCESS(rc) + && wndSelection + && wndSelection != m_wndCur) + { + char *pszWndName = wndX11GetNameA(wndSelection); + AssertPtr(pszWndName); + logInfo("New guest source window %#x ('%s')\n", wndSelection, pszWndName); + + /* Start over. */ + reset(); + + /* Map the window on the current cursor position, which should provoke + * an XdndEnter event. */ + rc = proxyWinShow(); + if (RT_SUCCESS(rc)) + { + rc = mouseCursorFakeMove(); + if (RT_SUCCESS(rc)) + { + bool fWaitFailed = false; /* Waiting for status changed failed? */ + + /* Wait until we're in "Dragging" state. */ + rc = waitForStatusChange(Dragging, 100 /* 100ms timeout */); + + /* + * Note: Don't wait too long here, as this mostly will make + * the drag and drop experience on the host being laggy + * and unresponsive. + * + * Instead, let the host query multiple times with 100ms + * timeout each (see above) and only report an error if + * the overall querying time has been exceeded.< + */ + if (RT_SUCCESS(rc)) + { + m_enmMode = GH; + } + else if (rc == VERR_TIMEOUT) + { + /** @todo Make m_cFailedPendingAttempts configurable. For slower window managers? */ + if (m_cFailedPendingAttempts++ > 50) /* Tolerate up to 5s total (100ms for each slot). */ + fWaitFailed = true; + else + rc = VINF_SUCCESS; + } + else if (RT_FAILURE(rc)) + fWaitFailed = true; + + if (fWaitFailed) + { + logError("Error mapping proxy window to guest source window %#x ('%s'), rc=%Rrc\n", + wndSelection, pszWndName, rc); + + /* Reset the counter in any case. */ + m_cFailedPendingAttempts = 0; + } + } + } + + RTStrFree(pszWndName); + } + else + logInfo("No guest source window\n"); + } + + /* + * Acknowledge to the host in any case, regardless + * if something failed here or not. Be responsive. + */ + + int rc2 = RTCritSectEnter(&m_dataCS); + if (RT_SUCCESS(rc2)) + { + RTCString strFormatsCur = gX11->xAtomListToString(m_lstFormats); + if (!strFormatsCur.isEmpty()) + { + strFormats = strFormatsCur; + dndActionDefault = VBOX_DND_ACTION_COPY; /** @todo Handle default action! */ + dndActionList = VBOX_DND_ACTION_COPY; /** @todo Ditto. */ + dndActionList |= toHGCMActions(m_lstActions); + } + + RTCritSectLeave(&m_dataCS); + } + + rc2 = VbglR3DnDGHSendAckPending(&m_dndCtx, dndActionDefault, dndActionList, + strFormats.c_str(), strFormats.length() + 1 /* Include termination */); + LogFlowThisFunc(("uClientID=%RU32, dndActionDefault=0x%x, dndActionList=0x%x, strFormats=%s, rc=%Rrc\n", + m_dndCtx.uClientID, dndActionDefault, dndActionList, strFormats.c_str(), rc2)); + if (RT_FAILURE(rc2)) + { + logError("Error reporting pending drag and drop operation status to host: %Rrc\n", rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Guest -> Host: Event signalling that the host has dropped the item(s) on the + * host side. + * + * @returns IPRT status code. + * @param strFormat Requested format to send to the host. + * @param dndActionRequested Requested action to perform on the guest. + */ +int DragInstance::ghDropped(const RTCString &strFormat, VBOXDNDACTION dndActionRequested) +{ + LogFlowThisFunc(("mode=%RU32, state=%RU32, strFormat=%s, dndActionRequested=0x%x\n", + m_enmMode, m_enmState, strFormat.c_str(), dndActionRequested)); + + /* Currently in wrong mode? Bail out. */ + if ( m_enmMode == Unknown + || m_enmMode == HG) + { + return VERR_INVALID_STATE; + } + + if ( m_enmMode == GH + && m_enmState != Dragging) + { + return VERR_INVALID_STATE; + } + + int rc = VINF_SUCCESS; + + m_enmState = Dropped; + +#ifdef DEBUG + XWindowAttributes xwa; + XGetWindowAttributes(m_pDisplay, m_wndCur, &xwa); + LogFlowThisFunc(("wndProxy=%RU32, wndCur=%RU32, x=%d, y=%d, width=%d, height=%d\n", + m_wndProxy.hWnd, m_wndCur, xwa.x, xwa.y, xwa.width, xwa.height)); + + Window wndSelection = XGetSelectionOwner(m_pDisplay, xAtom(XA_XdndSelection)); + LogFlowThisFunc(("wndSelection=%#x\n", wndSelection)); +#endif + + /* We send a fake mouse move event to the current window, cause + * this should have the grab. */ + mouseCursorFakeMove(); + + /** + * The fake button release event above should lead to a XdndDrop event from the + * source window. Because of showing our proxy window, other Xdnd events can + * occur before, e.g. a XdndPosition event. We are not interested + * in those, so just try to get the right one. + */ + + XClientMessageEvent evDnDDrop; + bool fDrop = waitForX11ClientMsg(evDnDDrop, xAtom(XA_XdndDrop), 5 * 1000 /* 5s timeout */); + if (fDrop) + { + LogFlowThisFunc(("XA_XdndDrop\n")); + + /* Request to convert the selection in the specific format and + * place it to our proxy window as property. */ + Assert(evDnDDrop.message_type == xAtom(XA_XdndDrop)); + + Window wndSource = evDnDDrop.data.l[XdndDropWindow]; /* Source window which has sent the message. */ + Assert(wndSource == m_wndCur); + + Atom aFormat = gX11->stringToxAtom(strFormat.c_str()); + + Time tsDrop; + if (m_curVer >= 1) + tsDrop = evDnDDrop.data.l[XdndDropTimeStamp]; + else + tsDrop = CurrentTime; + + XConvertSelection(m_pDisplay, xAtom(XA_XdndSelection), aFormat, xAtom(XA_XdndSelection), + m_wndProxy.hWnd, tsDrop); + + /* Wait for the selection notify event. */ + XEvent evSelNotify; + RT_ZERO(evSelNotify); + if (waitForX11Msg(evSelNotify, SelectionNotify, 5 * 1000 /* 5s timeout */)) + { + bool fCancel = false; + + /* Make some paranoid checks. */ + if ( evSelNotify.xselection.type == SelectionNotify + && evSelNotify.xselection.display == m_pDisplay + && evSelNotify.xselection.selection == xAtom(XA_XdndSelection) + && evSelNotify.xselection.requestor == m_wndProxy.hWnd + && evSelNotify.xselection.target == aFormat) + { + LogFlowThisFunc(("Selection notfiy (from wnd=%#x)\n", m_wndCur)); + + Atom aPropType; + int iPropFormat; + unsigned long cItems, cbRemaining; + unsigned char *pcData = NULL; + int xRc = XGetWindowProperty(m_pDisplay, m_wndProxy.hWnd, + xAtom(XA_XdndSelection) /* Property */, + 0 /* Offset */, + VBOX_MAX_XPROPERTIES /* Length of 32-bit multiples */, + True /* Delete property? */, + AnyPropertyType, /* Property type */ + &aPropType, &iPropFormat, &cItems, &cbRemaining, &pcData); + if (xRc != Success) + logError("Error getting XA_XdndSelection property of proxy window=%#x: %s\n", + m_wndProxy.hWnd, gX11->xErrorToString(xRc).c_str()); + + LogFlowThisFunc(("strType=%s, iPropFormat=%d, cItems=%RU32, cbRemaining=%RU32\n", + gX11->xAtomToString(aPropType).c_str(), iPropFormat, cItems, cbRemaining)); + + if ( aPropType != None + && pcData != NULL + && iPropFormat >= 8 + && cItems > 0 + && cbRemaining == 0) + { + size_t cbData = cItems * (iPropFormat / 8); + LogFlowThisFunc(("cbData=%zu\n", cbData)); + + /* For whatever reason some of the string MIME types are not + * zero terminated. Check that and correct it when necessary, + * because the guest side wants this in any case. */ + if ( m_lstAllowedFormats.contains(strFormat) + && pcData[cbData - 1] != '\0') + { + unsigned char *pvDataTmp = static_cast<unsigned char*>(RTMemAlloc(cbData + 1)); + if (pvDataTmp) + { + memcpy(pvDataTmp, pcData, cbData); + pvDataTmp[cbData++] = '\0'; + + rc = VbglR3DnDGHSendData(&m_dndCtx, strFormat.c_str(), pvDataTmp, cbData); + RTMemFree(pvDataTmp); + } + else + rc = VERR_NO_MEMORY; + } + else + { + /* Send the raw data to the host. */ + rc = VbglR3DnDGHSendData(&m_dndCtx, strFormat.c_str(), pcData, cbData); + LogFlowThisFunc(("Sent strFormat=%s, rc=%Rrc\n", strFormat.c_str(), rc)); + } + + if (RT_SUCCESS(rc)) + { + rc = m_wndProxy.sendFinished(wndSource, dndActionRequested); + } + else + fCancel = true; + } + else + { + if (aPropType == xAtom(XA_INCR)) + { + /** @todo Support incremental transfers. */ + AssertMsgFailed(("Incremental transfers are not supported yet\n")); + + logError("Incremental transfers are not supported yet\n"); + rc = VERR_NOT_IMPLEMENTED; + } + else + { + logError("Not supported data type: %s\n", gX11->xAtomToString(aPropType).c_str()); + rc = VERR_NOT_SUPPORTED; + } + + fCancel = true; + } + + if (fCancel) + { + logInfo("Cancelling dropping to host\n"); + + /* Cancel the operation -- inform the source window by + * sending a XdndFinished message so that the source can toss the required data. */ + rc = m_wndProxy.sendFinished(wndSource, VBOX_DND_ACTION_IGNORE); + } + + /* Cleanup. */ + if (pcData) + XFree(pcData); + } + else + rc = VERR_INVALID_PARAMETER; + } + else + rc = VERR_TIMEOUT; + } + else + rc = VERR_TIMEOUT; + + /* Inform the host on error. */ + if (RT_FAILURE(rc)) + { + int rc2 = VbglR3DnDGHSendError(&m_dndCtx, rc); + LogFlowThisFunc(("Sending error %Rrc to host resulted in %Rrc\n", rc, rc2)); NOREF(rc2); + /* This is not fatal for us, just ignore. */ + } + + /* At this point, we have either successfully transfered any data or not. + * So reset our internal state because we are done here for the current (ongoing) + * drag and drop operation. */ + reset(); + + LogFlowFuncLeaveRC(rc); + return rc; +} +#endif /* VBOX_WITH_DRAG_AND_DROP_GH */ + +/* + * Helpers + */ + +/** + * Fakes moving the mouse cursor to provoke various drag and drop + * events such as entering a target window or moving within a + * source window. + * + * Not the most elegant and probably correct function, but does + * the work for now. + * + * @returns IPRT status code. + */ +int DragInstance::mouseCursorFakeMove(void) const +{ + int iScreenID = XDefaultScreen(m_pDisplay); + /** @todo What about multiple screens? Test this! */ + + const int iScrX = XDisplayWidth(m_pDisplay, iScreenID); + const int iScrY = XDisplayHeight(m_pDisplay, iScreenID); + + int fx, fy, rx, ry; + Window wndTemp, wndChild; + int wx, wy; unsigned int mask; + XQueryPointer(m_pDisplay, m_wndRoot, &wndTemp, &wndChild, &rx, &ry, &wx, &wy, &mask); + + /* + * Apply some simple clipping and change the position slightly. + */ + + /* FakeX */ + if (rx == 0) fx = 1; + else if (rx == iScrX) fx = iScrX - 1; + else fx = rx + 1; + + /* FakeY */ + if (ry == 0) fy = 1; + else if (ry == iScrY) fy = iScrY - 1; + else fy = ry + 1; + + /* + * Move the cursor to trigger the wanted events. + */ + LogFlowThisFunc(("cursorRootX=%d, cursorRootY=%d\n", fx, fy)); + int rc = mouseCursorMove(fx, fy); + if (RT_SUCCESS(rc)) + { + /* Move the cursor back to its original position. */ + rc = mouseCursorMove(rx, ry); + } + + return rc; +} + +/** + * Moves the mouse pointer to a specific position. + * + * @returns IPRT status code. + * @param iPosX Absolute X coordinate. + * @param iPosY Absolute Y coordinate. + */ +int DragInstance::mouseCursorMove(int iPosX, int iPosY) const +{ + int iScreenID = XDefaultScreen(m_pDisplay); + /** @todo What about multiple screens? Test this! */ + + const int iScrX = XDisplayWidth(m_pDisplay, iScreenID); + const int iScrY = XDisplayHeight(m_pDisplay, iScreenID); + + iPosX = RT_CLAMP(iPosX, 0, iScrX); + iPosY = RT_CLAMP(iPosY, 0, iScrY); + + LogFlowThisFunc(("iPosX=%d, iPosY=%d\n", iPosX, iPosY)); + + /* Move the guest pointer to the DnD position, so we can find the window + * below that position. */ + XWarpPointer(m_pDisplay, None, m_wndRoot, 0, 0, 0, 0, iPosX, iPosY); + return VINF_SUCCESS; +} + +/** + * Sends a mouse button event to a specific window. + * + * @param wndDest Window to send the mouse button event to. + * @param rx X coordinate relative to the root window's origin. + * @param ry Y coordinate relative to the root window's origin. + * @param iButton Mouse button to press/release. + * @param fPress Whether to press or release the mouse button. + */ +void DragInstance::mouseButtonSet(Window wndDest, int rx, int ry, int iButton, bool fPress) +{ + LogFlowThisFunc(("wndDest=%#x, rx=%d, ry=%d, iBtn=%d, fPress=%RTbool\n", + wndDest, rx, ry, iButton, fPress)); + +#ifdef VBOX_DND_WITH_XTEST + /** @todo Make this check run only once. */ + int ev, er, ma, mi; + if (XTestQueryExtension(m_pDisplay, &ev, &er, &ma, &mi)) + { + LogFlowThisFunc(("XText extension available\n")); + + int xRc = XTestFakeButtonEvent(m_pDisplay, 1, fPress ? True : False, CurrentTime); + if (Rc == 0) + logError("Error sending XTestFakeButtonEvent event: %s\n", gX11->xErrorToString(xRc).c_str()); + XFlush(m_pDisplay); + } + else + { +#endif + LogFlowThisFunc(("Note: XText extension not available or disabled\n")); + + unsigned int mask = 0; + + if ( rx == -1 + && ry == -1) + { + Window wndRoot, wndChild; + int wx, wy; + XQueryPointer(m_pDisplay, m_wndRoot, &wndRoot, &wndChild, &rx, &ry, &wx, &wy, &mask); + LogFlowThisFunc(("Mouse pointer is at root x=%d, y=%d\n", rx, ry)); + } + + XButtonEvent eBtn; + RT_ZERO(eBtn); + + eBtn.display = m_pDisplay; + eBtn.root = m_wndRoot; + eBtn.window = wndDest; + eBtn.subwindow = None; + eBtn.same_screen = True; + eBtn.time = CurrentTime; + eBtn.button = iButton; + eBtn.state = mask | (iButton == 1 ? Button1MotionMask : + iButton == 2 ? Button2MotionMask : + iButton == 3 ? Button3MotionMask : + iButton == 4 ? Button4MotionMask : + iButton == 5 ? Button5MotionMask : 0); + eBtn.type = fPress ? ButtonPress : ButtonRelease; + eBtn.send_event = False; + eBtn.x_root = rx; + eBtn.y_root = ry; + + XTranslateCoordinates(m_pDisplay, eBtn.root, eBtn.window, eBtn.x_root, eBtn.y_root, &eBtn.x, &eBtn.y, &eBtn.subwindow); + LogFlowThisFunc(("state=0x%x, x=%d, y=%d\n", eBtn.state, eBtn.x, eBtn.y)); + + int xRc = XSendEvent(m_pDisplay, wndDest, True /* fPropagate */, + ButtonPressMask, + reinterpret_cast<XEvent*>(&eBtn)); + if (xRc == 0) + logError("Error sending XButtonEvent event to window=%#x: %s\n", wndDest, gX11->xErrorToString(xRc).c_str()); + + XFlush(m_pDisplay); + +#ifdef VBOX_DND_WITH_XTEST + } +#endif +} + +/** + * Shows the (invisible) proxy window. The proxy window is needed for intercepting + * drags from the host to the guest or from the guest to the host. It acts as a proxy + * between the host and the actual (UI) element on the guest OS. + * + * To not make it miss any actions this window gets spawned across the entire guest + * screen (think of an umbrella) to (hopefully) capture everything. A proxy window + * which follows the cursor would be far too slow here. + * + * @returns IPRT status code. + * @param piRootX X coordinate relative to the root window's origin. Optional. + * @param piRootY Y coordinate relative to the root window's origin. Optional. + */ +int DragInstance::proxyWinShow(int *piRootX /* = NULL */, int *piRootY /* = NULL */) const +{ + /* piRootX is optional. */ + /* piRootY is optional. */ + + LogFlowThisFuncEnter(); + + int rc = VINF_SUCCESS; + +#if 0 +# ifdef VBOX_DND_WITH_XTEST + XTestGrabControl(m_pDisplay, False); +# endif +#endif + + /* Get the mouse pointer position and determine if we're on the same screen as the root window + * and return the current child window beneath our mouse pointer, if any. */ + int iRootX, iRootY; + int iChildX, iChildY; + unsigned int iMask; + Window wndRoot, wndChild; + Bool fInRootWnd = XQueryPointer(m_pDisplay, m_wndRoot, &wndRoot, &wndChild, + &iRootX, &iRootY, &iChildX, &iChildY, &iMask); + + LogFlowThisFunc(("fInRootWnd=%RTbool, wndRoot=%RU32, wndChild=%RU32, iRootX=%d, iRootY=%d\n", + RT_BOOL(fInRootWnd), wndRoot, wndChild, iRootX, iRootY)); NOREF(fInRootWnd); + + if (piRootX) + *piRootX = iRootX; + if (piRootY) + *piRootY = iRootY; + + XSynchronize(m_pDisplay, True /* Enable sync */); + + /* Bring our proxy window into foreground. */ + XMapWindow(m_pDisplay, m_wndProxy.hWnd); + XRaiseWindow(m_pDisplay, m_wndProxy.hWnd); + + /* Spawn our proxy window over the entire screen, making it an easy drop target for the host's cursor. */ + LogFlowThisFunc(("Proxy window x=%d, y=%d, width=%d, height=%d\n", + m_wndProxy.iX, m_wndProxy.iY, m_wndProxy.iWidth, m_wndProxy.iHeight)); + XMoveResizeWindow(m_pDisplay, m_wndProxy.hWnd, m_wndProxy.iX, m_wndProxy.iY, m_wndProxy.iWidth, m_wndProxy.iHeight); + + XFlush(m_pDisplay); + + XSynchronize(m_pDisplay, False /* Disable sync */); + +#if 0 +# ifdef VBOX_DND_WITH_XTEST + XTestGrabControl(m_pDisplay, True); +# endif +#endif + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Hides the (invisible) proxy window. + */ +int DragInstance::proxyWinHide(void) +{ + LogFlowFuncEnter(); + + XUnmapWindow(m_pDisplay, m_wndProxy.hWnd); + XFlush(m_pDisplay); + + m_eventQueueList.clear(); + + return VINF_SUCCESS; /** @todo Add error checking. */ +} + +/** + * Allocates the name (title) of an X window. + * The returned pointer must be freed using RTStrFree(). + * + * @returns Pointer to the allocated window name. + * @param wndThis Window to retrieve name for. + * + * @remark If the window title is not available, the text + * "<No name>" will be returned. + */ +char *DragInstance::wndX11GetNameA(Window wndThis) const +{ + char *pszName = NULL; + + XTextProperty propName; + if (XGetWMName(m_pDisplay, wndThis, &propName)) + { + if (propName.value) + pszName = RTStrDup((char *)propName.value); /** @todo UTF8? */ + XFree(propName.value); + } + + if (!pszName) /* No window name found? */ + pszName = RTStrDup("<No name>"); + + return pszName; +} + +/** + * Clear a window's supported/accepted actions list. + * + * @param wndThis Window to clear the list for. + */ +void DragInstance::wndXDnDClearActionList(Window wndThis) const +{ + XDeleteProperty(m_pDisplay, wndThis, xAtom(XA_XdndActionList)); +} + +/** + * Clear a window's supported/accepted formats list. + * + * @param wndThis Window to clear the list for. + */ +void DragInstance::wndXDnDClearFormatList(Window wndThis) const +{ + XDeleteProperty(m_pDisplay, wndThis, xAtom(XA_XdndTypeList)); +} + +/** + * Retrieves a window's supported/accepted XDnD actions. + * + * @returns IPRT status code. + * @param wndThis Window to retrieve the XDnD actions for. + * @param lstActions Reference to VBoxDnDAtomList to store the action into. + */ +int DragInstance::wndXDnDGetActionList(Window wndThis, VBoxDnDAtomList &lstActions) const +{ + Atom iActType = None; + int iActFmt; + unsigned long cItems, cbData; + unsigned char *pcbData = NULL; + + /* Fetch the possible list of actions, if this property is set. */ + int xRc = XGetWindowProperty(m_pDisplay, wndThis, + xAtom(XA_XdndActionList), + 0, VBOX_MAX_XPROPERTIES, + False, XA_ATOM, &iActType, &iActFmt, &cItems, &cbData, &pcbData); + if (xRc != Success) + { + LogFlowThisFunc(("Error getting XA_XdndActionList atoms from window=%#x: %s\n", + wndThis, gX11->xErrorToString(xRc).c_str())); + return VERR_NOT_FOUND; + } + + LogFlowThisFunc(("wndThis=%#x, cItems=%RU32, pcbData=%p\n", wndThis, cItems, pcbData)); + + if (cItems > 0) + { + AssertPtr(pcbData); + Atom *paData = reinterpret_cast<Atom *>(pcbData); + + for (unsigned i = 0; i < RT_MIN(VBOX_MAX_XPROPERTIES, cItems); i++) + { + LogFlowThisFunc(("\t%s\n", gX11->xAtomToString(paData[i]).c_str())); + lstActions.append(paData[i]); + } + + XFree(pcbData); + } + + return VINF_SUCCESS; +} + +/** + * Retrieves a window's supported/accepted XDnD formats. + * + * @returns IPRT status code. + * @param wndThis Window to retrieve the XDnD formats for. + * @param lstTypes Reference to VBoxDnDAtomList to store the formats into. + */ +int DragInstance::wndXDnDGetFormatList(Window wndThis, VBoxDnDAtomList &lstTypes) const +{ + Atom iActType = None; + int iActFmt; + unsigned long cItems, cbData; + unsigned char *pcbData = NULL; + + int xRc = XGetWindowProperty(m_pDisplay, wndThis, + xAtom(XA_XdndTypeList), + 0, VBOX_MAX_XPROPERTIES, + False, XA_ATOM, &iActType, &iActFmt, &cItems, &cbData, &pcbData); + if (xRc != Success) + { + LogFlowThisFunc(("Error getting XA_XdndTypeList atoms from window=%#x: %s\n", + wndThis, gX11->xErrorToString(xRc).c_str())); + return VERR_NOT_FOUND; + } + + LogFlowThisFunc(("wndThis=%#x, cItems=%RU32, pcbData=%p\n", wndThis, cItems, pcbData)); + + if (cItems > 0) + { + AssertPtr(pcbData); + Atom *paData = reinterpret_cast<Atom *>(pcbData); + + for (unsigned i = 0; i < RT_MIN(VBOX_MAX_XPROPERTIES, cItems); i++) + { + LogFlowThisFunc(("\t%s\n", gX11->xAtomToString(paData[i]).c_str())); + lstTypes.append(paData[i]); + } + + XFree(pcbData); + } + + return VINF_SUCCESS; +} + +/** + * Sets (replaces) a window's XDnD accepted/allowed actions. + * + * @returns IPRT status code. + * @param wndThis Window to set the format list for. + * @param lstActions Reference to list of XDnD actions to set. + * + * @remark + */ +int DragInstance::wndXDnDSetActionList(Window wndThis, const VBoxDnDAtomList &lstActions) const +{ + if (lstActions.isEmpty()) + return VINF_SUCCESS; + + XChangeProperty(m_pDisplay, wndThis, + xAtom(XA_XdndActionList), + XA_ATOM, 32, PropModeReplace, + reinterpret_cast<const unsigned char*>(lstActions.raw()), + lstActions.size()); + + return VINF_SUCCESS; +} + +/** + * Sets (replaces) a window's XDnD accepted format list. + * + * @returns IPRT status code. + * @param wndThis Window to set the format list for. + * @param atmProp Property to set. + * @param lstFormats Reference to list of XDnD formats to set. + */ +int DragInstance::wndXDnDSetFormatList(Window wndThis, Atom atmProp, const VBoxDnDAtomList &lstFormats) const +{ + if (lstFormats.isEmpty()) + return VERR_INVALID_PARAMETER; + + /* We support TARGETS and the data types. */ + VBoxDnDAtomList lstFormatsExt(lstFormats.size() + 1); + lstFormatsExt.append(xAtom(XA_TARGETS)); + lstFormatsExt.append(lstFormats); + + /* Add the property with the property data to the window. */ + XChangeProperty(m_pDisplay, wndThis, atmProp, + XA_ATOM, 32, PropModeReplace, + reinterpret_cast<const unsigned char*>(lstFormatsExt.raw()), + lstFormatsExt.size()); + + return VINF_SUCCESS; +} + +/** + * Converts a RTCString list to VBoxDnDAtomList list. + * + * @returns IPRT status code. + * @param lstFormats Reference to RTCString list to convert. + * @param lstAtoms Reference to VBoxDnDAtomList list to store results in. + */ +int DragInstance::toAtomList(const RTCList<RTCString> &lstFormats, VBoxDnDAtomList &lstAtoms) const +{ + for (size_t i = 0; i < lstFormats.size(); ++i) + lstAtoms.append(XInternAtom(m_pDisplay, lstFormats.at(i).c_str(), False)); + + return VINF_SUCCESS; +} + +/** + * Converts a raw-data string list to VBoxDnDAtomList list. + * + * @returns IPRT status code. + * @param pvData Pointer to string data to convert. + * @param cbData Size (in bytes) to convert. + * @param lstAtoms Reference to VBoxDnDAtomList list to store results in. + */ +int DragInstance::toAtomList(const void *pvData, uint32_t cbData, VBoxDnDAtomList &lstAtoms) const +{ + RT_NOREF1(lstAtoms); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + + const char *pszStr = (char *)pvData; + uint32_t cbStr = cbData; + + int rc = VINF_SUCCESS; + + VBoxDnDAtomList lstAtom; + while (cbStr) + { + size_t cbSize = RTStrNLen(pszStr, cbStr); + + /* Create a copy with max N chars, so that we are on the save side, + * even if the data isn't zero terminated. */ + char *pszTmp = RTStrDupN(pszStr, cbSize); + if (!pszTmp) + { + rc = VERR_NO_MEMORY; + break; + } + + lstAtom.append(XInternAtom(m_pDisplay, pszTmp, False)); + RTStrFree(pszTmp); + + pszStr += cbSize + 1; + cbStr -= cbSize + 1; + } + + return rc; +} + +/** + * Converts a HGCM-based drag'n drop action to a Atom-based drag'n drop action. + * + * @returns Converted Atom-based drag'n drop action. + * @param dndAction HGCM drag'n drop actions to convert. + */ +/* static */ +Atom DragInstance::toAtomAction(VBOXDNDACTION dndAction) +{ + /* Ignore is None. */ + return (isDnDCopyAction(dndAction) ? xAtom(XA_XdndActionCopy) : + isDnDMoveAction(dndAction) ? xAtom(XA_XdndActionMove) : + isDnDLinkAction(dndAction) ? xAtom(XA_XdndActionLink) : + None); +} + +/** + * Converts HGCM-based drag'n drop actions to a VBoxDnDAtomList list. + * + * @returns IPRT status code. + * @param dndActionList HGCM drag'n drop actions to convert. + * @param lstAtoms Reference to VBoxDnDAtomList to store actions in. + */ +/* static */ +int DragInstance::toAtomActions(VBOXDNDACTIONLIST dndActionList, VBoxDnDAtomList &lstAtoms) +{ + if (hasDnDCopyAction(dndActionList)) + lstAtoms.append(xAtom(XA_XdndActionCopy)); + if (hasDnDMoveAction(dndActionList)) + lstAtoms.append(xAtom(XA_XdndActionMove)); + if (hasDnDLinkAction(dndActionList)) + lstAtoms.append(xAtom(XA_XdndActionLink)); + + return VINF_SUCCESS; +} + +/** + * Converts an Atom-based drag'n drop action to a HGCM drag'n drop action. + * + * @returns HGCM drag'n drop action. + * @param atom Atom-based drag'n drop action to convert. + */ +/* static */ +uint32_t DragInstance::toHGCMAction(Atom atom) +{ + uint32_t uAction = VBOX_DND_ACTION_IGNORE; + + if (atom == xAtom(XA_XdndActionCopy)) + uAction = VBOX_DND_ACTION_COPY; + else if (atom == xAtom(XA_XdndActionMove)) + uAction = VBOX_DND_ACTION_MOVE; + else if (atom == xAtom(XA_XdndActionLink)) + uAction = VBOX_DND_ACTION_LINK; + + return uAction; +} + +/** + * Converts an VBoxDnDAtomList list to an HGCM action list. + * + * @returns ORed HGCM action list. + * @param lstActions List of Atom-based actions to convert. + */ +/* static */ +uint32_t DragInstance::toHGCMActions(const VBoxDnDAtomList &lstActions) +{ + uint32_t uActions = VBOX_DND_ACTION_IGNORE; + + for (size_t i = 0; i < lstActions.size(); i++) + uActions |= toHGCMAction(lstActions.at(i)); + + return uActions; +} + +/******************************************************************************* + * VBoxDnDProxyWnd implementation. + ******************************************************************************/ + +VBoxDnDProxyWnd::VBoxDnDProxyWnd(void) + : pDisp(NULL) + , hWnd(0) + , iX(0) + , iY(0) + , iWidth(0) + , iHeight(0) +{ + +} + +VBoxDnDProxyWnd::~VBoxDnDProxyWnd(void) +{ + destroy(); +} + +int VBoxDnDProxyWnd::init(Display *pDisplay) +{ + /** @todo What about multiple screens? Test this! */ + int iScreenID = XDefaultScreen(pDisplay); + + iWidth = XDisplayWidth(pDisplay, iScreenID); + iHeight = XDisplayHeight(pDisplay, iScreenID); + pDisp = pDisplay; + + return VINF_SUCCESS; +} + +void VBoxDnDProxyWnd::destroy(void) +{ + +} + +int VBoxDnDProxyWnd::sendFinished(Window hWndSource, VBOXDNDACTION dndAction) +{ + /* Was the drop accepted by the host? That is, anything than ignoring. */ + bool fDropAccepted = dndAction > VBOX_DND_ACTION_IGNORE; + + LogFlowFunc(("dndAction=0x%x\n", dndAction)); + + /* Confirm the result of the transfer to the target window. */ + XClientMessageEvent m; + RT_ZERO(m); + m.type = ClientMessage; + m.display = pDisp; + m.window = hWnd; + m.message_type = xAtom(XA_XdndFinished); + m.format = 32; + m.data.l[XdndFinishedWindow] = hWnd; /* Target window. */ + m.data.l[XdndFinishedFlags] = fDropAccepted ? RT_BIT(0) : 0; /* Was the drop accepted? */ + m.data.l[XdndFinishedAction] = fDropAccepted ? DragInstance::toAtomAction(dndAction) : None; /* Action used on accept. */ + + int xRc = XSendEvent(pDisp, hWndSource, True, NoEventMask, reinterpret_cast<XEvent*>(&m)); + if (xRc == 0) + { + LogRel(("DnD: Error sending XA_XdndFinished event to source window=%#x: %s\n", + hWndSource, gX11->xErrorToString(xRc).c_str())); + + return VERR_GENERAL_FAILURE; /** @todo Fudge. */ + } + + return VINF_SUCCESS; +} + +/******************************************************************************* + * DragAndDropService implementation. + ******************************************************************************/ + +/** + * Initializes the drag and drop service. + * + * @returns IPRT status code. + */ +int DragAndDropService::init(void) +{ + LogFlowFuncEnter(); + + /* Initialise the guest library. */ + int rc = VbglR3InitUser(); + if (RT_FAILURE(rc)) + { + VBClFatalError(("DnD: Failed to connect to the VirtualBox kernel service, rc=%Rrc\n", rc)); + return rc; + } + + /* Connect to the x11 server. */ + m_pDisplay = XOpenDisplay(NULL); + if (!m_pDisplay) + { + VBClFatalError(("DnD: Unable to connect to X server -- running in a terminal session?\n")); + return VERR_NOT_FOUND; + } + + xHelpers *pHelpers = xHelpers::getInstance(m_pDisplay); + if (!pHelpers) + return VERR_NO_MEMORY; + + do + { + rc = RTSemEventCreate(&m_hEventSem); + if (RT_FAILURE(rc)) + break; + + rc = RTCritSectInit(&m_eventQueueCS); + if (RT_FAILURE(rc)) + break; + + /* Event thread for events coming from the HGCM device. */ + rc = RTThreadCreate(&m_hHGCMThread, hgcmEventThread, this, + 0, RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE, "dndHGCM"); + if (RT_FAILURE(rc)) + break; + + rc = RTThreadUserWait(m_hHGCMThread, 10 * 1000 /* 10s timeout */); + if (RT_FAILURE(rc)) + break; + + if (ASMAtomicReadBool(&m_fSrvStopping)) + break; + + /* Event thread for events coming from the x11 system. */ + rc = RTThreadCreate(&m_hX11Thread, x11EventThread, this, + 0, RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE, "dndX11"); + if (RT_FAILURE(rc)) + break; + + rc = RTThreadUserWait(m_hX11Thread, 10 * 1000 /* 10s timeout */); + if (RT_FAILURE(rc)) + break; + + if (ASMAtomicReadBool(&m_fSrvStopping)) + break; + + } while (0); + + if (m_fSrvStopping) + rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */ + + if (RT_FAILURE(rc)) + LogRel(("DnD: Failed to initialize, rc=%Rrc\n", rc)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Main loop for the drag and drop service which does the HGCM message + * processing and routing to the according drag and drop instance(s). + * + * @returns IPRT status code. + * @param fDaemonised Whether to run in daemonized or not. Does not + * apply for this service. + */ +int DragAndDropService::run(bool fDaemonised /* = false */) +{ + RT_NOREF1(fDaemonised); + LogFlowThisFunc(("fDaemonised=%RTbool\n", fDaemonised)); + + int rc; + do + { + m_pCurDnD = new DragInstance(m_pDisplay, this); + if (!m_pCurDnD) + { + rc = VERR_NO_MEMORY; + break; + } + + /* Note: For multiple screen support in VBox it is not necessary to use + * another screen number than zero. Maybe in the future it will become + * necessary if VBox supports multiple X11 screens. */ + rc = m_pCurDnD->init(0 /* uScreenID */); + /* Note: Can return VINF_PERMISSION_DENIED if HGCM host service is not available. */ + if (rc != VINF_SUCCESS) + { + if (RT_FAILURE(rc)) + LogRel(("DnD: Unable to connect to drag and drop service, rc=%Rrc\n", rc)); + else if (rc == VINF_PERMISSION_DENIED) + LogRel(("DnD: Not available on host, terminating\n")); + break; + } + + LogRel(("DnD: Started\n")); + LogRel2(("DnD: %sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr())); + + /* Enter the main event processing loop. */ + do + { + DnDEvent e; + RT_ZERO(e); + + LogFlowFunc(("Waiting for new event ...\n")); + rc = RTSemEventWait(m_hEventSem, RT_INDEFINITE_WAIT); + if (RT_FAILURE(rc)) + break; + + AssertMsg(m_eventQueue.size(), ("Event queue is empty when it shouldn't\n")); + + e = m_eventQueue.first(); + m_eventQueue.removeFirst(); + + if (e.enmType == DnDEvent::DnDEventType_HGCM) + { + PVBGLR3DNDEVENT pVbglR3Event = e.hgcm; + AssertPtrBreak(pVbglR3Event); + + LogFlowThisFunc(("HGCM event, enmType=%RU32\n", pVbglR3Event->enmType)); + switch (pVbglR3Event->enmType) + { + case VBGLR3DNDEVENTTYPE_HG_ENTER: + { + if (pVbglR3Event->u.HG_Enter.cbFormats) + { + RTCList<RTCString> lstFormats = + RTCString(pVbglR3Event->u.HG_Enter.pszFormats, pVbglR3Event->u.HG_Enter.cbFormats - 1).split("\r\n"); + rc = m_pCurDnD->hgEnter(lstFormats, pVbglR3Event->u.HG_Enter.dndLstActionsAllowed); + if (RT_FAILURE(rc)) + break; + /* Enter is always followed by a move event. */ + } + else + { + AssertMsgFailed(("cbFormats is 0\n")); + rc = VERR_INVALID_PARAMETER; + break; + } + + /* Note: After HOST_DND_HG_EVT_ENTER there immediately is a move + * event, so fall through is intentional here. */ + RT_FALL_THROUGH(); + } + + case VBGLR3DNDEVENTTYPE_HG_MOVE: + { + rc = m_pCurDnD->hgMove(pVbglR3Event->u.HG_Move.uXpos, pVbglR3Event->u.HG_Move.uYpos, + pVbglR3Event->u.HG_Move.dndActionDefault); + break; + } + + case VBGLR3DNDEVENTTYPE_HG_LEAVE: + { + rc = m_pCurDnD->hgLeave(); + break; + } + + case VBGLR3DNDEVENTTYPE_HG_DROP: + { + rc = m_pCurDnD->hgDrop(pVbglR3Event->u.HG_Drop.uXpos, pVbglR3Event->u.HG_Drop.uYpos, + pVbglR3Event->u.HG_Drop.dndActionDefault); + break; + } + + /* Note: VbglR3DnDRecvNextMsg() will return HOST_DND_HG_SND_DATA_HDR when + * the host has finished copying over all the data to the guest. + * + * The actual data transfer (and message processing for it) will be done + * internally by VbglR3DnDRecvNextMsg() to not duplicate any code for different + * platforms. + * + * The data header now will contain all the (meta) data the guest needs in + * order to complete the DnD operation. */ + case VBGLR3DNDEVENTTYPE_HG_RECEIVE: + { + rc = m_pCurDnD->hgDataReceive(&pVbglR3Event->u.HG_Received.Meta); + break; + } + + case VBGLR3DNDEVENTTYPE_HG_CANCEL: + { + m_pCurDnD->reset(); /** @todo Test this! */ + break; + } + +#ifdef VBOX_WITH_DRAG_AND_DROP_GH + case VBGLR3DNDEVENTTYPE_GH_ERROR: + { + m_pCurDnD->reset(); + break; + } + + case VBGLR3DNDEVENTTYPE_GH_REQ_PENDING: + { + rc = m_pCurDnD->ghIsDnDPending(); + break; + } + + case VBGLR3DNDEVENTTYPE_GH_DROP: + { + rc = m_pCurDnD->ghDropped(pVbglR3Event->u.GH_Drop.pszFormat, pVbglR3Event->u.GH_Drop.dndActionRequested); + break; + } +#endif + default: + { + m_pCurDnD->logError("Received unsupported message '%RU32'\n", pVbglR3Event->enmType); + rc = VERR_NOT_SUPPORTED; + break; + } + } + + LogFlowFunc(("Message %RU32 processed with %Rrc\n", pVbglR3Event->enmType, rc)); + if (RT_FAILURE(rc)) + { + /* Tell the user. */ + m_pCurDnD->logError("Processing message %RU32 failed with %Rrc\n", pVbglR3Event->enmType, rc); + + /* If anything went wrong, do a reset and start over. */ + m_pCurDnD->reset(); + } + + VbglR3DnDEventFree(e.hgcm); + e.hgcm = NULL; + } + else if (e.enmType == DnDEvent::DnDEventType_X11) + { + m_pCurDnD->onX11Event(e.x11); + } + else + AssertMsgFailed(("Unknown event queue type %RU32\n", e.enmType)); + + /* + * Make sure that any X11 requests have actually been sent to the + * server, since we are waiting for responses using poll() on + * another thread which will not automatically trigger flushing. + */ + XFlush(m_pDisplay); + + } while (!ASMAtomicReadBool(&m_fSrvStopping)); + + LogRel(("DnD: Stopped with rc=%Rrc\n", rc)); + + } while (0); + + if (m_pCurDnD) + { + delete m_pCurDnD; + m_pCurDnD = NULL; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +void DragAndDropService::cleanup(void) +{ + LogFlowFuncEnter(); + + LogRel2(("DnD: Terminating threads ...\n")); + + ASMAtomicXchgBool(&m_fSrvStopping, true); + + /* + * Wait for threads to terminate. + */ + int rcThread, rc2; + if (m_hHGCMThread != NIL_RTTHREAD) + { +#if 0 /** @todo Does not work because we don't cancel the HGCM call! */ + rc2 = RTThreadWait(m_hHGCMThread, 30 * 1000 /* 30s timeout */, &rcThread); +#else + rc2 = RTThreadWait(m_hHGCMThread, 200 /* 200ms timeout */, &rcThread); +#endif + if (RT_SUCCESS(rc2)) + rc2 = rcThread; + + if (RT_FAILURE(rc2)) + LogRel(("DnD: Error waiting for HGCM thread to terminate: %Rrc\n", rc2)); + } + + if (m_hX11Thread != NIL_RTTHREAD) + { +#if 0 + rc2 = RTThreadWait(m_hX11Thread, 30 * 1000 /* 30s timeout */, &rcThread); +#else + rc2 = RTThreadWait(m_hX11Thread, 200 /* 200ms timeout */, &rcThread); +#endif + if (RT_SUCCESS(rc2)) + rc2 = rcThread; + + if (RT_FAILURE(rc2)) + LogRel(("DnD: Error waiting for X11 thread to terminate: %Rrc\n", rc2)); + } + + LogRel2(("DnD: Terminating threads done\n")); + + xHelpers::destroyInstance(); + + VbglR3Term(); +} + +/** + * Static callback function for HGCM message processing thread. An internal + * message queue will be filled which then will be processed by the according + * drag'n drop instance. + * + * @returns IPRT status code. + * @param hThread Thread handle to use. + * @param pvUser Pointer to DragAndDropService instance to use. + */ +/* static */ +DECLCALLBACK(int) DragAndDropService::hgcmEventThread(RTTHREAD hThread, void *pvUser) +{ + AssertPtrReturn(pvUser, VERR_INVALID_PARAMETER); + DragAndDropService *pThis = static_cast<DragAndDropService*>(pvUser); + AssertPtr(pThis); + + /* This thread has an own DnD context, e.g. an own client ID. */ + VBGLR3GUESTDNDCMDCTX dndCtx; + + /* + * Initialize thread. + */ + int rc = VbglR3DnDConnect(&dndCtx); + + /* Set stop indicator on failure. */ + if (RT_FAILURE(rc)) + ASMAtomicXchgBool(&pThis->m_fSrvStopping, true); + + /* Let the service instance know in any case. */ + int rc2 = RTThreadUserSignal(hThread); + AssertRC(rc2); + + if (RT_FAILURE(rc)) + return rc; + + /* Number of invalid messages skipped in a row. */ + int cMsgSkippedInvalid = 0; + DnDEvent e; + + do + { + RT_ZERO(e); + e.enmType = DnDEvent::DnDEventType_HGCM; + + /* Wait for new events. */ + rc = VbglR3DnDEventGetNext(&dndCtx, &e.hgcm); + if (RT_SUCCESS(rc)) + { + cMsgSkippedInvalid = 0; /* Reset skipped messages count. */ + pThis->m_eventQueue.append(e); + + rc = RTSemEventSignal(pThis->m_hEventSem); + if (RT_FAILURE(rc)) + break; + } + else + { + LogRel(("DnD: Processing next message failed with rc=%Rrc\n", rc)); + + /* Old(er) hosts either are broken regarding DnD support or otherwise + * don't support the stuff we do on the guest side, so make sure we + * don't process invalid messages forever. */ + if (cMsgSkippedInvalid++ > 32) + { + LogRel(("DnD: Too many invalid/skipped messages from host, exiting ...\n")); + break; + } + } + + } while (!ASMAtomicReadBool(&pThis->m_fSrvStopping)); + + VbglR3DnDDisconnect(&dndCtx); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Static callback function for X11 message processing thread. All X11 messages + * will be directly routed to the according drag'n drop instance. + * + * @returns IPRT status code. + * @param hThread Thread handle to use. + * @param pvUser Pointer to DragAndDropService instance to use. + */ +/* static */ +DECLCALLBACK(int) DragAndDropService::x11EventThread(RTTHREAD hThread, void *pvUser) +{ + AssertPtrReturn(pvUser, VERR_INVALID_PARAMETER); + DragAndDropService *pThis = static_cast<DragAndDropService*>(pvUser); + AssertPtr(pThis); + + int rc = VINF_SUCCESS; + + /* Note: Nothing to initialize here (yet). */ + + /* Set stop indicator on failure. */ + if (RT_FAILURE(rc)) + ASMAtomicXchgBool(&pThis->m_fSrvStopping, true); + + /* Let the service instance know in any case. */ + int rc2 = RTThreadUserSignal(hThread); + AssertRC(rc2); + + DnDEvent e; + do + { + /* + * Wait for new events. We can't use XIfEvent here, cause this locks + * the window connection with a mutex and if no X11 events occurs this + * blocks any other calls we made to X11. So instead check for new + * events and if there are not any new one, sleep for a certain amount + * of time. + */ + if (XEventsQueued(pThis->m_pDisplay, QueuedAfterFlush) > 0) + { + RT_ZERO(e); + e.enmType = DnDEvent::DnDEventType_X11; + + /* XNextEvent will block until a new X event becomes available. */ + XNextEvent(pThis->m_pDisplay, &e.x11); + { +#ifdef DEBUG + switch (e.x11.type) + { + case ClientMessage: + { + XClientMessageEvent *pEvent = reinterpret_cast<XClientMessageEvent*>(&e); + AssertPtr(pEvent); + + RTCString strType = xAtomToString(pEvent->message_type); + LogFlowFunc(("ClientMessage: %s from wnd=%#x\n", strType.c_str(), pEvent->window)); + break; + } + + default: + LogFlowFunc(("Received X event type=%d\n", e.x11.type)); + break; + } +#endif + /* At the moment we only have one drag instance. */ + DragInstance *pInstance = pThis->m_pCurDnD; + AssertPtr(pInstance); + + pInstance->onX11Event(e.x11); + } + } + else + RTThreadSleep(25 /* ms */); + + } while (!ASMAtomicReadBool(&pThis->m_fSrvStopping)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** Drag and drop magic number, start of a UUID. */ +#define DRAGANDDROPSERVICE_MAGIC 0x67c97173 + +/** VBoxClient service class wrapping the logic for the service while + * the main VBoxClient code provides the daemon logic needed by all services. + */ +struct DRAGANDDROPSERVICE +{ + /** The service interface. */ + struct VBCLSERVICE *pInterface; + /** Magic number for sanity checks. */ + uint32_t uMagic; + /** Service object. */ + DragAndDropService mDragAndDrop; +}; + +static const char *getPidFilePath() +{ + return ".vboxclient-draganddrop.pid"; +} + +static int init(struct VBCLSERVICE **ppInterface) +{ + struct DRAGANDDROPSERVICE *pSelf = (struct DRAGANDDROPSERVICE *)ppInterface; + + if (pSelf->uMagic != DRAGANDDROPSERVICE_MAGIC) + VBClFatalError(("Bad DnD service object!\n")); + return pSelf->mDragAndDrop.init(); +} + +static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised) +{ + struct DRAGANDDROPSERVICE *pSelf = (struct DRAGANDDROPSERVICE *)ppInterface; + + if (pSelf->uMagic != DRAGANDDROPSERVICE_MAGIC) + VBClFatalError(("Bad DnD service object!\n")); + return pSelf->mDragAndDrop.run(fDaemonised); +} + +static void cleanup(struct VBCLSERVICE **ppInterface) +{ + struct DRAGANDDROPSERVICE *pSelf = (struct DRAGANDDROPSERVICE *)ppInterface; + + if (pSelf->uMagic != DRAGANDDROPSERVICE_MAGIC) + VBClFatalError(("Bad DnD service object!\n")); + return pSelf->mDragAndDrop.cleanup(); +} + +struct VBCLSERVICE vbclDragAndDropInterface = +{ + getPidFilePath, + init, + run, + cleanup +}; + +/* Static factory. */ +struct VBCLSERVICE **VBClGetDragAndDropService(void) +{ + struct DRAGANDDROPSERVICE *pService = + (struct DRAGANDDROPSERVICE *)RTMemAlloc(sizeof(*pService)); + + if (!pService) + VBClFatalError(("Out of memory\n")); + pService->pInterface = &vbclDragAndDropInterface; + pService->uMagic = DRAGANDDROPSERVICE_MAGIC; + new(&pService->mDragAndDrop) DragAndDropService(); + return &pService->pInterface; +} diff --git a/src/VBox/Additions/x11/VBoxClient/hostversion.cpp b/src/VBox/Additions/x11/VBoxClient/hostversion.cpp new file mode 100644 index 00000000..489e715e --- /dev/null +++ b/src/VBox/Additions/x11/VBoxClient/hostversion.cpp @@ -0,0 +1,227 @@ +/* $Id: hostversion.cpp $ */ +/** @file + * X11 guest client - host version check. + */ + +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ +#include <stdio.h> +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include <iprt/mem.h> +#include <iprt/ldr.h> +#include <iprt/string.h> +#include <iprt/thread.h> + +#ifdef VBOX_WITH_DBUS +# include <VBox/dbus.h> +#endif +#include <VBox/log.h> +#include <VBox/VBoxGuestLib.h> +#ifdef VBOX_OSE +# include <VBox/version.h> +#endif + +#include "VBoxClient.h" + +static const char *getPidFilePath() +{ + return ".vboxclient-hostversion.pid"; +} + +static int showNotify(const char *pszHeader, const char *pszBody) +{ + int rc; +# ifdef VBOX_WITH_DBUS + DBusConnection *conn; + DBusMessage* msg = NULL; + conn = dbus_bus_get (DBUS_BUS_SESSION, NULL); + if (conn == NULL) + { + LogRelFlowFunc(("Could not retrieve D-BUS session bus!\n")); + rc = VERR_INVALID_HANDLE; + } + else + { + msg = dbus_message_new_method_call("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + "Notify"); + if (msg == NULL) + { + LogRel(("Could not create D-BUS message!\n")); + rc = VERR_INVALID_HANDLE; + } + else + rc = VINF_SUCCESS; + } + if (RT_SUCCESS(rc)) + { + uint32_t msg_replace_id = 0; + const char *msg_app = "VBoxClient"; + const char *msg_icon = ""; + const char *msg_summary = pszHeader; + const char *msg_body = pszBody; + int32_t msg_timeout = -1; /* Let the notification server decide */ + + DBusMessageIter iter; + DBusMessageIter array; + /*DBusMessageIter dict; - unused */ + /*DBusMessageIter value; - unused */ + /*DBusMessageIter variant; - unused */ + /*DBusMessageIter data; - unused */ + + /* Format: UINT32 org.freedesktop.Notifications.Notify + * (STRING app_name, UINT32 replaces_id, STRING app_icon, STRING summary, STRING body, + * ARRAY actions, DICT hints, INT32 expire_timeout) + */ + dbus_message_iter_init_append(msg,&iter); + dbus_message_iter_append_basic(&iter,DBUS_TYPE_STRING,&msg_app); + dbus_message_iter_append_basic(&iter,DBUS_TYPE_UINT32,&msg_replace_id); + dbus_message_iter_append_basic(&iter,DBUS_TYPE_STRING,&msg_icon); + dbus_message_iter_append_basic(&iter,DBUS_TYPE_STRING,&msg_summary); + dbus_message_iter_append_basic(&iter,DBUS_TYPE_STRING,&msg_body); + dbus_message_iter_open_container(&iter,DBUS_TYPE_ARRAY,DBUS_TYPE_STRING_AS_STRING,&array); + dbus_message_iter_close_container(&iter,&array); + dbus_message_iter_open_container(&iter,DBUS_TYPE_ARRAY,"{sv}",&array); + dbus_message_iter_close_container(&iter,&array); + dbus_message_iter_append_basic(&iter,DBUS_TYPE_INT32,&msg_timeout); + + DBusError err; + dbus_error_init(&err); + + DBusMessage *reply; + reply = dbus_connection_send_with_reply_and_block(conn, msg, 30 * 1000 /* 30 seconds timeout */, &err); + if (dbus_error_is_set(&err)) + LogRel(("D-BUS returned an error while sending the notification: %s", err.message)); + else if (reply) + { + dbus_connection_flush(conn); + dbus_message_unref(reply); + } + if (dbus_error_is_set(&err)) + dbus_error_free(&err); + } + if (msg != NULL) + dbus_message_unref(msg); +# else + /** @todo Implement me */ + RT_NOREF(pszHeader, pszBody); + rc = VINF_SUCCESS; +# endif /* VBOX_WITH_DBUS */ + return rc; +} + +/** @todo Move this part in VbglR3 and just provide a callback for the platform-specific + notification stuff, since this is very similar to the VBoxTray code. */ +static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised) +{ + int rc; + LogFlowFunc(("\n")); + + NOREF(ppInterface); + /* Initialise the guest library. */ + rc = VbglR3InitUser(); + if (RT_FAILURE(rc)) + VBClFatalError(("Failed to connect to the VirtualBox kernel service, rc=%Rrc\n", rc)); + /* Because we need desktop notifications to be displayed, wait + * some time to make the desktop environment load (as a work around). */ + if (fDaemonised) + RTThreadSleep(30 * 1000 /* Wait 30 seconds */); + +# ifdef VBOX_WITH_DBUS + rc = RTDBusLoadLib(); + if (RT_FAILURE(rc)) + LogRel(("VBoxClient: D-Bus seems not to be installed; no host version check/notification done.\n")); +# else + rc = VERR_NOT_IMPLEMENTED; +# endif /* VBOX_WITH_DBUS */ + +# ifdef VBOX_WITH_GUEST_PROPS + uint32_t uGuestPropSvcClientID; + if (RT_SUCCESS(rc)) + { + rc = VbglR3GuestPropConnect(&uGuestPropSvcClientID); + if (RT_FAILURE(rc)) + LogRel(("VBoxClient: Cannot connect to guest property service while chcking for host version! rc = %Rrc\n", rc)); + } + + if (RT_SUCCESS(rc)) + { + char *pszHostVersion; + char *pszGuestVersion; + bool bUpdate; + + rc = VbglR3HostVersionCheckForUpdate(uGuestPropSvcClientID, &bUpdate, &pszHostVersion, &pszGuestVersion); + if (RT_SUCCESS(rc)) + { + if (bUpdate) + { + char szMsg[1024]; + char szTitle[64]; + + /** @todo add some translation macros here */ + RTStrPrintf(szTitle, sizeof(szTitle), "VirtualBox Guest Additions update available!"); +#ifndef VBOX_OSE + RTStrPrintf(szMsg, sizeof(szMsg), "Your guest is currently running the Guest Additions version %s. " + "We recommend updating to the latest version (%s) by choosing the " + "install option from the Devices menu.", pszGuestVersion, pszHostVersion); +#else +/* This is the message which appears for non-Oracle builds of the +* Guest Additions. Distributors are encouraged to customise this. */ + RTStrPrintf(szMsg, sizeof(szMsg), "Your virtual machine is currently running the Guest Additions version %s. Since you are running a version of the Guest Additions provided by the operating system you installed in the virtual machine we recommend that you update it to at least version %s using that system's update features, or alternatively that you remove this version and then install the " VBOX_VENDOR_SHORT " Guest Additions package using the install option from the Devices menu. Please consult the documentation for the operating system you are running to find out how to update or remove the current Guest Additions package.", pszGuestVersion, pszHostVersion); +#endif + rc = showNotify(szTitle, szMsg); + LogRel(("VBoxClient: VirtualBox Guest Additions update available!")); + if (RT_FAILURE(rc)) + LogRel(("VBoxClient: Could not show version notifier tooltip! rc = %d\n", rc)); + } + + /* Store host version to not notify again */ + rc = VbglR3HostVersionLastCheckedStore(uGuestPropSvcClientID, pszHostVersion); + + VbglR3GuestPropReadValueFree(pszHostVersion); + VbglR3GuestPropReadValueFree(pszGuestVersion); + } + VbglR3GuestPropDisconnect(uGuestPropSvcClientID); + } +# endif /* VBOX_WITH_GUEST_PROPS */ + VbglR3Term(); + LogFlowFunc(("returning %Rrc\n", rc)); + return rc; +} + +struct VBCLSERVICE vbclHostVersionInterface = +{ + getPidFilePath, + VBClServiceDefaultHandler, /* init */ + run, + VBClServiceDefaultCleanup +}; + +struct HOSTVERSIONSERVICE +{ + struct VBCLSERVICE *pInterface; +}; + +/* Static factory */ +struct VBCLSERVICE **VBClGetHostVersionService() +{ + struct HOSTVERSIONSERVICE *pService = + (struct HOSTVERSIONSERVICE *)RTMemAlloc(sizeof(*pService)); + + if (!pService) + VBClFatalError(("Out of memory\n")); + pService->pInterface = &vbclHostVersionInterface; + return &pService->pInterface; +} + diff --git a/src/VBox/Additions/x11/VBoxClient/main.cpp b/src/VBox/Additions/x11/VBoxClient/main.cpp new file mode 100644 index 00000000..b5429fbc --- /dev/null +++ b/src/VBox/Additions/x11/VBoxClient/main.cpp @@ -0,0 +1,425 @@ +/* $Id: main.cpp $ */ +/** @file + * VirtualBox Guest Additions - X11 Client. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <sys/types.h> +#include <sys/wait.h> +#include <stdlib.h> /* For exit */ +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <poll.h> +#include <signal.h> + +#include <X11/Xlib.h> +#include <X11/Xatom.h> + +#include <iprt/buildconfig.h> +#include <iprt/critsect.h> +#include <iprt/env.h> +#include <iprt/file.h> +#include <iprt/initterm.h> +#include <iprt/message.h> +#include <iprt/path.h> +#include <iprt/param.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/types.h> +#include <VBox/VBoxGuestLib.h> +#include <VBox/err.h> +#include <VBox/log.h> + +#include "VBoxClient.h" + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/*static int (*gpfnOldIOErrorHandler)(Display *) = NULL; - unused */ + +/** Object representing the service we are running. This has to be global + * so that the cleanup routine can access it. */ +struct VBCLSERVICE **g_pService; +/** The name of our pidfile. It is global for the benefit of the cleanup + * routine. */ +static char g_szPidFile[RTPATH_MAX] = ""; +/** The file handle of our pidfile. It is global for the benefit of the + * cleanup routine. */ +static RTFILE g_hPidFile; +/** Global critical section held during the clean-up routine (to prevent it + * being called on multiple threads at once) or things which may not happen + * during clean-up (e.g. pausing and resuming the service). + */ +RTCRITSECT g_critSect; +/** Counter of how often our deamon has been respawned. */ +unsigned cRespawn = 0; + + + +/** + * Exit with a fatal error. + * + * This is used by the VBClFatalError macro and thus needs to be external. + */ +void vbclFatalError(char *pszMessage) +{ + char *pszCommand; + int status; + if (pszMessage && cRespawn == 0) + { + pszCommand = RTStrAPrintf2("notify-send \"VBoxClient: %s\"", pszMessage); + if (pszCommand) + { + status = system(pszCommand); + if (WEXITSTATUS(status) != 0) /* Utility or extension not available. */ + { + pszCommand = RTStrAPrintf2("xmessage -buttons OK:0 -center \"VBoxClient: %s\"", + pszMessage); + if (pszCommand) + { + status = system(pszCommand); + if (WEXITSTATUS(status) != 0) /* Utility or extension not available. */ + { + RTPrintf("VBoxClient: %s", pszMessage); + } + } + } + } + } + _exit(RTEXITCODE_FAILURE); +} + +/** + * Clean up if we get a signal or something. + * + * This is extern so that we can call it from other compilation units. + */ +void VBClCleanUp(bool fExit /*=true*/) +{ + /* We never release this, as we end up with a call to exit(3) which is not + * async-safe. Unless we fix this application properly, we should be sure + * never to exit from anywhere except from this method. */ + int rc = RTCritSectEnter(&g_critSect); + if (RT_FAILURE(rc)) + VBClFatalError(("VBoxClient: Failure while acquiring the global critical section, rc=%Rrc\n", rc)); + if (g_pService) + (*g_pService)->cleanup(g_pService); + if (g_szPidFile[0] && g_hPidFile) + VbglR3ClosePidFile(g_szPidFile, g_hPidFile); + if (fExit) + exit(RTEXITCODE_SUCCESS); +} + +/** + * A standard signal handler which cleans up and exits. + */ +static void vboxClientSignalHandler(int cSignal) +{ + LogRel(("VBoxClient: terminated with signal %d\n", cSignal)); + /** Disable seamless mode */ + RTPrintf(("VBoxClient: terminating...\n")); + VBClCleanUp(); +} + +/** + * Xlib error handler for certain errors that we can't avoid. + */ +static int vboxClientXLibErrorHandler(Display *pDisplay, XErrorEvent *pError) +{ + char errorText[1024]; + + XGetErrorText(pDisplay, pError->error_code, errorText, sizeof(errorText)); + LogRelFlow(("VBoxClient: an X Window protocol error occurred: %s (error code %d). Request code: %d, minor code: %d, serial number: %d\n", errorText, pError->error_code, pError->request_code, pError->minor_code, pError->serial)); + return 0; +} + +/** + * Xlib error handler for fatal errors. This often means that the programme is still running + * when X exits. + */ +static int vboxClientXLibIOErrorHandler(Display *pDisplay) +{ + RT_NOREF1(pDisplay); + LogRel(("VBoxClient: a fatal guest X Window error occurred. This may just mean that the Window system was shut down while the client was still running.\n")); + VBClCleanUp(); + return 0; /* We should never reach this. */ +} + +/** + * Reset all standard termination signals to call our signal handler, which + * cleans up and exits. + */ +static void vboxClientSetSignalHandlers(void) +{ + struct sigaction sigAction; + + LogRelFlowFunc(("\n")); + sigAction.sa_handler = vboxClientSignalHandler; + sigemptyset(&sigAction.sa_mask); + sigAction.sa_flags = 0; + sigaction(SIGHUP, &sigAction, NULL); + sigaction(SIGINT, &sigAction, NULL); + sigaction(SIGQUIT, &sigAction, NULL); + sigaction(SIGPIPE, &sigAction, NULL); + sigaction(SIGALRM, &sigAction, NULL); + sigaction(SIGTERM, &sigAction, NULL); + sigaction(SIGUSR1, &sigAction, NULL); + sigaction(SIGUSR2, &sigAction, NULL); + LogRelFlowFunc(("returning\n")); +} + +/** + * Print out a usage message and exit with success. + */ +static void vboxClientUsage(const char *pcszFileName) +{ + RTPrintf("Usage: %s --clipboard|" +#ifdef VBOX_WITH_DRAG_AND_DROP + "--draganddrop|" +#endif + "--display|" +# ifdef VBOX_WITH_GUEST_PROPS + "--checkhostversion|" +#endif + "--seamless|check3d|" + "--vmsvga|--vmsvga-x11" + "[-d|--nodaemon]\n", pcszFileName); + RTPrintf("Starts the VirtualBox DRM/X Window System guest services.\n\n"); + RTPrintf("Options:\n"); + RTPrintf(" --clipboard starts the shared clipboard service\n"); +#ifdef VBOX_WITH_DRAG_AND_DROP + RTPrintf(" --draganddrop starts the drag and drop service\n"); +#endif + RTPrintf(" --display starts the display management service\n"); +#ifdef VBOX_WITH_GUEST_PROPS + RTPrintf(" --checkhostversion starts the host version notifier service\n"); +#endif + RTPrintf(" --check3d tests whether 3D pass-through is enabled\n"); + RTPrintf(" --seamless starts the seamless windows service\n"); + RTPrintf(" --vmsvga starts VMSVGA dynamic resizing for DRM\n"); + RTPrintf(" --vmsvga-x11 starts VMSVGA dynamic resizing for X11\n"); + RTPrintf(" -f, --foreground run in the foreground (no daemonizing)\n"); + RTPrintf(" -d, --nodaemon continues running as a system service\n"); + RTPrintf(" -h, --help shows this help text\n"); + RTPrintf(" -V, --version shows version information\n"); + RTPrintf("\n"); +} + +/** + * Complains about seeing more than one service specification. + * + * @returns RTEXITCODE_SYNTAX. + */ +static int vbclSyntaxOnlyOneService(void) +{ + RTMsgError("More than one service specified! Only one, please."); + return RTEXITCODE_SYNTAX; +} + +/** + * The main loop for the VBoxClient daemon. + * @todo Clean up for readability. + */ +int main(int argc, char *argv[]) +{ + /* Initialise our runtime before all else. */ + int rc = RTR3InitExe(argc, &argv, 0); + if (RT_FAILURE(rc)) + return RTMsgInitFailure(rc); + + /* This should never be called twice in one process - in fact one Display + * object should probably never be used from multiple threads anyway. */ + if (!XInitThreads()) + VBClFatalError(("Failed to initialize X11 threads\n")); + + /* Get our file name for usage info and hints. */ + const char *pcszFileName = RTPathFilename(argv[0]); + if (!pcszFileName) + pcszFileName = "VBoxClient"; + + /* Parse our option(s) */ + /** @todo Use RTGetOpt() if the arguments become more complex. */ + bool fDaemonise = true; + bool fRespawn = true; + for (int i = 1; i < argc; ++i) + { + if ( !strcmp(argv[i], "-f") + || !strcmp(argv[i], "--foreground") + || !strcmp(argv[i], "-d") + || !strcmp(argv[i], "--nodaemon")) + { + /* If the user is running in "no daemon" mode anyway, send critical + * logging to stdout as well. */ + /** @todo r=bird: Since the release logger isn't created until the service + * calls VbglR3InitUser or VbglR3Init or RTLogCreate, this whole + * exercise is pointless. Added --init-vbgl-user and --init-vbgl-full + * for getting some work done. */ + PRTLOGGER pReleaseLog = RTLogRelGetDefaultInstance(); + if (pReleaseLog) + rc = RTLogDestinations(pReleaseLog, "stdout"); + if (pReleaseLog && RT_FAILURE(rc)) + return RTMsgErrorExitFailure("failed to redivert error output, rc=%Rrc", rc); + + fDaemonise = false; + if ( !strcmp(argv[i], "-f") + || !strcmp(argv[i], "--foreground")) + fRespawn = false; + } + else if (!strcmp(argv[i], "--no-respawn")) + { + fRespawn = false; + } + else if (!strcmp(argv[i], "--clipboard")) + { + if (g_pService) + return vbclSyntaxOnlyOneService(); + g_pService = VBClGetClipboardService(); + } + else if (!strcmp(argv[i], "--display")) + { + if (g_pService) + return vbclSyntaxOnlyOneService(); + g_pService = VBClGetDisplayService(); + } + else if (!strcmp(argv[i], "--seamless")) + { + if (g_pService) + return vbclSyntaxOnlyOneService(); + g_pService = VBClGetSeamlessService(); + } + else if (!strcmp(argv[i], "--checkhostversion")) + { + if (g_pService) + return vbclSyntaxOnlyOneService(); + g_pService = VBClGetHostVersionService(); + } +#ifdef VBOX_WITH_DRAG_AND_DROP + else if (!strcmp(argv[i], "--draganddrop")) + { + if (g_pService) + return vbclSyntaxOnlyOneService(); + g_pService = VBClGetDragAndDropService(); + } +#endif /* VBOX_WITH_DRAG_AND_DROP */ + else if (!strcmp(argv[i], "--check3d")) + { + if (g_pService) + return vbclSyntaxOnlyOneService(); + g_pService = VBClCheck3DService(); + } + else if (!strcmp(argv[i], "--vmsvga")) + { + if (g_pService) + return vbclSyntaxOnlyOneService(); + g_pService = VBClDisplaySVGAService(); + } + else if (!strcmp(argv[i], "--vmsvga-x11")) + { + if (g_pService) + break; + g_pService = VBClDisplaySVGAX11Service(); + } + /* bird: this is just a quick hack to get something out of the LogRel statements in the code. */ + else if (!strcmp(argv[i], "--init-vbgl-user")) + { + rc = VbglR3InitUser(); + if (RT_FAILURE(rc)) + return RTMsgErrorExitFailure("VbglR3InitUser failed: %Rrc", rc); + } + else if (!strcmp(argv[i], "--init-vbgl-full")) + { + rc = VbglR3Init(); + if (RT_FAILURE(rc)) + return RTMsgErrorExitFailure("VbglR3Init failed: %Rrc", rc); + } + else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) + { + vboxClientUsage(pcszFileName); + return RTEXITCODE_SUCCESS; + } + else if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version")) + { + RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr()); + return RTEXITCODE_SUCCESS; + } + else + { + RTMsgError("unrecognized option `%s'", argv[i]); + RTMsgInfo("Try `%s --help' for more information", pcszFileName); + return RTEXITCODE_SYNTAX; + } + } + if (!g_pService) + { + RTMsgError("No service specified. Quitting because nothing to do!"); + return RTEXITCODE_SYNTAX; + } + + rc = RTCritSectInit(&g_critSect); + if (RT_FAILURE(rc)) + VBClFatalError(("Initialising critical section failed: %Rrc\n", rc)); + if ((*g_pService)->getPidFilePath) + { + rc = RTPathUserHome(g_szPidFile, sizeof(g_szPidFile)); + if (RT_FAILURE(rc)) + VBClFatalError(("Getting home directory for PID file failed: %Rrc\n", rc)); + rc = RTPathAppend(g_szPidFile, sizeof(g_szPidFile), + (*g_pService)->getPidFilePath()); + if (RT_FAILURE(rc)) + VBClFatalError(("Creating PID file path failed: %Rrc\n", rc)); + if (fDaemonise) + rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */, fRespawn, &cRespawn); + if (RT_FAILURE(rc)) + VBClFatalError(("Daemonizing failed: %Rrc\n", rc)); + if (g_szPidFile[0]) + rc = VbglR3PidFile(g_szPidFile, &g_hPidFile); + if (rc == VERR_FILE_LOCK_VIOLATION) /* Already running. */ + return RTEXITCODE_SUCCESS; + if (RT_FAILURE(rc)) + VBClFatalError(("Creating PID file failed: %Rrc\n", rc)); + } + /* Set signal handlers to clean up on exit. */ + vboxClientSetSignalHandlers(); +#ifndef VBOXCLIENT_WITHOUT_X11 + /* Set an X11 error handler, so that we don't die when we get unavoidable + * errors. */ + XSetErrorHandler(vboxClientXLibErrorHandler); + /* Set an X11 I/O error handler, so that we can shutdown properly on + * fatal errors. */ + XSetIOErrorHandler(vboxClientXLibIOErrorHandler); +#endif + rc = (*g_pService)->init(g_pService); + if (RT_SUCCESS(rc)) + { + rc = (*g_pService)->run(g_pService, fDaemonise); + if (RT_FAILURE(rc)) + LogRel2(("Running service failed: %Rrc\n", rc)); + } + else + { + /** @todo r=andy Should we return an appropriate exit code if the service failed to init? + * Must be tested carefully with our init scripts first. */ + LogRel2(("Initializing service failed: %Rrc\n", rc)); + } + VBClCleanUp(false /*fExit*/); + return RTEXITCODE_SUCCESS; +} + diff --git a/src/VBox/Additions/x11/VBoxClient/seamless-x11.cpp b/src/VBox/Additions/x11/VBoxClient/seamless-x11.cpp new file mode 100644 index 00000000..26c2cf53 --- /dev/null +++ b/src/VBox/Additions/x11/VBoxClient/seamless-x11.cpp @@ -0,0 +1,520 @@ +/* $Id: seamless-x11.cpp $ */ +/** @file + * X11 Seamless mode. + */ + +/* + * Copyright (C) 2008-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* Header files * +*********************************************************************************************************************************/ + +#include <iprt/errcore.h> +#include <iprt/assert.h> +#include <iprt/vector.h> +#include <VBox/log.h> + +#include "seamless-x11.h" +#include "VBoxClient.h" + +#include <X11/Xatom.h> +#include <X11/Xmu/WinUtil.h> + +#include <limits.h> + +#ifdef TESTCASE +#undef DefaultRootWindow +#define DefaultRootWindow XDefaultRootWindow +#endif + +/***************************************************************************** +* Static functions * +*****************************************************************************/ + +static unsigned char *XXGetProperty (Display *aDpy, Window aWnd, Atom aPropType, + const char *aPropName, unsigned long *nItems) +{ + LogRelFlowFunc(("\n")); + Atom propNameAtom = XInternAtom (aDpy, aPropName, + True /* only_if_exists */); + if (propNameAtom == None) + { + return NULL; + } + + Atom actTypeAtom = None; + int actFmt = 0; + unsigned long nBytesAfter = 0; + unsigned char *propVal = 0; + int rc = XGetWindowProperty (aDpy, aWnd, propNameAtom, + 0, LONG_MAX, False /* delete */, + aPropType, &actTypeAtom, &actFmt, + nItems, &nBytesAfter, &propVal); + if (rc != Success) + return NULL; + + LogRelFlowFunc(("returning\n")); + return propVal; +} + +/** + * Initialise the guest and ensure that it is capable of handling seamless mode + * + * @param pHostCallback host callback. + * @returns true if it can handle seamless, false otherwise + */ +int SeamlessX11::init(PFNSENDREGIONUPDATE pHostCallback) +{ + int rc = VINF_SUCCESS; + + LogRelFlowFunc(("\n")); + if (mHostCallback != NULL) /* Assertion */ + { + LogRel(("VBoxClient: ERROR: attempt to initialise seamless guest object twice!\n")); + return VERR_INTERNAL_ERROR; + } + if (!(mDisplay = XOpenDisplay(NULL))) + { + LogRel(("VBoxClient: seamless guest object failed to acquire a connection to the display.\n")); + return VERR_ACCESS_DENIED; + } + mHostCallback = pHostCallback; + mEnabled = false; + unmonitorClientList(); + LogRelFlowFunc(("returning %Rrc\n", rc)); + return rc; +} + +/** + * Read information about currently visible windows in the guest and subscribe to X11 + * events about changes to this information. + * + * @note This class does not contain its own event thread, so an external thread must + * call nextConfigurationEvent() for as long as events are wished. + * @todo This function should switch the guest to fullscreen mode. + */ +int SeamlessX11::start(void) +{ + int rc = VINF_SUCCESS; + /** Dummy values for XShapeQueryExtension */ + int error, event; + + LogRelFlowFunc(("\n")); + if (mEnabled) + return VINF_SUCCESS; + mSupportsShape = XShapeQueryExtension(mDisplay, &event, &error); + mEnabled = true; + monitorClientList(); + rebuildWindowTree(); + LogRelFlowFunc(("returning %Rrc\n", rc)); + return rc; +} + +/** Stop reporting seamless events to the host. Free information about guest windows + and stop requesting updates. */ +void SeamlessX11::stop(void) +{ + LogRelFlowFunc(("\n")); + if (!mEnabled) + return; + mEnabled = false; + unmonitorClientList(); + freeWindowTree(); + LogRelFlowFunc(("returning\n")); +} + +void SeamlessX11::monitorClientList(void) +{ + LogRelFlowFunc(("called\n")); + XSelectInput(mDisplay, DefaultRootWindow(mDisplay), PropertyChangeMask | SubstructureNotifyMask); +} + +void SeamlessX11::unmonitorClientList(void) +{ + LogRelFlowFunc(("called\n")); + XSelectInput(mDisplay, DefaultRootWindow(mDisplay), PropertyChangeMask); +} + +/** + * Recreate the table of toplevel windows of clients on the default root window of the + * X server. + */ +void SeamlessX11::rebuildWindowTree(void) +{ + LogRelFlowFunc(("called\n")); + freeWindowTree(); + addClients(DefaultRootWindow(mDisplay)); + mChanged = true; +} + + +/** + * Look at the list of children of a virtual root window and add them to the list of clients + * if they belong to a client which is not a virtual root. + * + * @param hRoot the virtual root window to be examined + */ +void SeamlessX11::addClients(const Window hRoot) +{ + /** Unused out parameters of XQueryTree */ + Window hRealRoot, hParent; + /** The list of children of the root supplied, raw pointer */ + Window *phChildrenRaw = NULL; + /** The list of children of the root supplied, auto-pointer */ + Window *phChildren; + /** The number of children of the root supplied */ + unsigned cChildren; + + LogRelFlowFunc(("\n")); + if (!XQueryTree(mDisplay, hRoot, &hRealRoot, &hParent, &phChildrenRaw, &cChildren)) + return; + phChildren = phChildrenRaw; + for (unsigned i = 0; i < cChildren; ++i) + addClientWindow(phChildren[i]); + XFree(phChildrenRaw); + LogRelFlowFunc(("returning\n")); +} + + +void SeamlessX11::addClientWindow(const Window hWin) +{ + LogRelFlowFunc(("\n")); + XWindowAttributes winAttrib; + bool fAddWin = true; + Window hClient = XmuClientWindow(mDisplay, hWin); + + if (isVirtualRoot(hClient)) + fAddWin = false; + if (fAddWin && !XGetWindowAttributes(mDisplay, hWin, &winAttrib)) + { + LogRelFunc(("VBoxClient: Failed to get the window attributes for window %d\n", hWin)); + fAddWin = false; + } + if (fAddWin && (winAttrib.map_state == IsUnmapped)) + fAddWin = false; + XSizeHints dummyHints; + long dummyLong; + /* Apparently (?) some old kwin versions had unwanted client windows + * without normal hints. */ + if (fAddWin && (!XGetWMNormalHints(mDisplay, hClient, &dummyHints, + &dummyLong))) + { + LogRelFlowFunc(("window %lu, client window %lu has no size hints\n", + hWin, hClient)); + fAddWin = false; + } + if (fAddWin) + { + XRectangle *pRects = NULL; + int cRects = 0, iOrdering; + bool hasShape = false; + + LogRelFlowFunc(("adding window %lu, client window %lu\n", hWin, + hClient)); + if (mSupportsShape) + { + XShapeSelectInput(mDisplay, hWin, ShapeNotifyMask); + pRects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects, &iOrdering); + if (!pRects) + cRects = 0; + else + { + if ( (cRects > 1) + || (pRects[0].x != 0) + || (pRects[0].y != 0) + || (pRects[0].width != winAttrib.width) + || (pRects[0].height != winAttrib.height) + ) + hasShape = true; + } + } + mGuestWindows.addWindow(hWin, hasShape, winAttrib.x, winAttrib.y, + winAttrib.width, winAttrib.height, cRects, + pRects); + } + LogRelFlowFunc(("returning\n")); +} + + +/** + * Checks whether a window is a virtual root. + * @returns true if it is, false otherwise + * @param hWin the window to be examined + */ +bool SeamlessX11::isVirtualRoot(Window hWin) +{ + unsigned char *windowTypeRaw = NULL; + Atom *windowType; + unsigned long ulCount; + bool rc = false; + + LogRelFlowFunc(("\n")); + windowTypeRaw = XXGetProperty(mDisplay, hWin, XA_ATOM, WM_TYPE_PROP, &ulCount); + if (windowTypeRaw != NULL) + { + windowType = (Atom *)(windowTypeRaw); + if ( (ulCount != 0) + && (*windowType == XInternAtom(mDisplay, WM_TYPE_DESKTOP_PROP, True))) + rc = true; + } + if (windowTypeRaw) + XFree(windowTypeRaw); + LogRelFlowFunc(("returning %RTbool\n", rc)); + return rc; +} + +DECLCALLBACK(int) VBoxGuestWinFree(VBoxGuestWinInfo *pInfo, void *pvParam) +{ + Display *pDisplay = (Display *)pvParam; + + XShapeSelectInput(pDisplay, pInfo->Core.Key, 0); + delete pInfo; + return VINF_SUCCESS; +} + +/** + * Free all information in the tree of visible windows + */ +void SeamlessX11::freeWindowTree(void) +{ + /* We use post-increment in the operation to prevent the iterator from being invalidated. */ + LogRelFlowFunc(("\n")); + mGuestWindows.detachAll(VBoxGuestWinFree, mDisplay); + LogRelFlowFunc(("returning\n")); +} + + +/** + * Waits for a position or shape-related event from guest windows + * + * @note Called from the guest event thread. + */ +void SeamlessX11::nextConfigurationEvent(void) +{ + XEvent event; + + LogRelFlowFunc(("\n")); + /* Start by sending information about the current window setup to the host. We do this + here because we want to send all such information from a single thread. */ + if (mChanged && mEnabled) + { + updateRects(); + mHostCallback(mpRects, mcRects); + } + mChanged = false; + /* We execute this even when seamless is disabled, as it also waits for + * enable and disable notification. */ + XNextEvent(mDisplay, &event); + if (!mEnabled) + return; + switch (event.type) + { + case ConfigureNotify: + { + XConfigureEvent *pConf = &event.xconfigure; + LogRelFlowFunc(("configure event, window=%lu, x=%i, y=%i, w=%i, h=%i, send_event=%RTbool\n", + (unsigned long) pConf->window, (int) pConf->x, + (int) pConf->y, (int) pConf->width, + (int) pConf->height, pConf->send_event)); + } + doConfigureEvent(event.xconfigure.window); + break; + case MapNotify: + LogRelFlowFunc(("map event, window=%lu, send_event=%RTbool\n", + (unsigned long) event.xmap.window, + event.xmap.send_event)); + rebuildWindowTree(); + break; + case PropertyNotify: + if ( event.xproperty.atom != XInternAtom(mDisplay, "_NET_CLIENT_LIST", True /* only_if_exists */) + || event.xproperty.window != DefaultRootWindow(mDisplay)) + break; + LogRelFlowFunc(("_NET_CLIENT_LIST property event on root window.\n")); + rebuildWindowTree(); + break; + case VBoxShapeNotify: /* This is defined wrong in my X11 header files! */ + LogRelFlowFunc(("shape event, window=%lu, send_event=%RTbool\n", + (unsigned long) event.xany.window, + event.xany.send_event)); + /* the window member in xany is in the same place as in the shape event */ + doShapeEvent(event.xany.window); + break; + case UnmapNotify: + LogRelFlowFunc(("unmap event, window=%lu, send_event=%RTbool\n", + (unsigned long) event.xunmap.window, + event.xunmap.send_event)); + rebuildWindowTree(); + break; + default: + break; + } + LogRelFlowFunc(("processed event\n")); +} + +/** + * Handle a configuration event in the seamless event thread by setting the new position. + * + * @param hWin the window to be examined + */ +void SeamlessX11::doConfigureEvent(Window hWin) +{ + VBoxGuestWinInfo *pInfo = mGuestWindows.find(hWin); + if (pInfo) + { + XWindowAttributes winAttrib; + + if (!XGetWindowAttributes(mDisplay, hWin, &winAttrib)) + return; + pInfo->mX = winAttrib.x; + pInfo->mY = winAttrib.y; + pInfo->mWidth = winAttrib.width; + pInfo->mHeight = winAttrib.height; + mChanged = true; + } +} + +/** + * Handle a window shape change event in the seamless event thread. + * + * @param hWin the window to be examined + */ +void SeamlessX11::doShapeEvent(Window hWin) +{ + LogRelFlowFunc(("\n")); + VBoxGuestWinInfo *pInfo = mGuestWindows.find(hWin); + if (pInfo) + { + XRectangle *pRects; + int cRects = 0, iOrdering; + + pRects = XShapeGetRectangles(mDisplay, hWin, ShapeBounding, &cRects, + &iOrdering); + if (!pRects) + cRects = 0; + pInfo->mhasShape = true; + if (pInfo->mpRects) + XFree(pInfo->mpRects); + pInfo->mcRects = cRects; + pInfo->mpRects = pRects; + mChanged = true; + } + LogRelFlowFunc(("returning\n")); +} + +/** + * Gets the list of visible rectangles + */ +RTRECT *SeamlessX11::getRects(void) +{ + return mpRects; +} + +/** + * Gets the number of rectangles in the visible rectangle list + */ +size_t SeamlessX11::getRectCount(void) +{ + return mcRects; +} + +RTVEC_DECL(RectList, RTRECT) + +DECLCALLBACK(int) getRectsCallback(VBoxGuestWinInfo *pInfo, + struct RectList *pRects) +{ + if (pInfo->mhasShape) + { + for (int i = 0; i < pInfo->mcRects; ++i) + { + RTRECT *pRect; + + pRect = RectListPushBack(pRects); + if (!pRect) + return VERR_NO_MEMORY; + pRect->xLeft = pInfo->mX + + pInfo->mpRects[i].x; + pRect->yBottom = pInfo->mY + + pInfo->mpRects[i].y + + pInfo->mpRects[i].height; + pRect->xRight = pInfo->mX + + pInfo->mpRects[i].x + + pInfo->mpRects[i].width; + pRect->yTop = pInfo->mY + + pInfo->mpRects[i].y; + } + } + else + { + RTRECT *pRect; + + pRect = RectListPushBack(pRects); + if (!pRect) + return VERR_NO_MEMORY; + pRect->xLeft = pInfo->mX; + pRect->yBottom = pInfo->mY + + pInfo->mHeight; + pRect->xRight = pInfo->mX + + pInfo->mWidth; + pRect->yTop = pInfo->mY; + } + return VINF_SUCCESS; +} + +/** + * Updates the list of seamless rectangles + */ +int SeamlessX11::updateRects(void) +{ + LogRelFlowFunc(("\n")); + struct RectList rects = RTVEC_INITIALIZER; + + if (mcRects != 0) + { + int rc = RectListReserve(&rects, mcRects * 2); + if (RT_FAILURE(rc)) + return rc; + } + mGuestWindows.doWithAll((PVBOXGUESTWINCALLBACK)getRectsCallback, + &rects); + if (mpRects) + RTMemFree(mpRects); + mcRects = RectListSize(&rects); + mpRects = RectListDetach(&rects); + LogRelFlowFunc(("returning\n")); + return VINF_SUCCESS; +} + +/** + * Send a client event to wake up the X11 seamless event loop prior to stopping it. + * + * @note This function should only be called from the host event thread. + */ +bool SeamlessX11::interruptEventWait(void) +{ + bool rc = false; + Display *pDisplay = XOpenDisplay(NULL); + + LogRelFlowFunc(("\n")); + if (pDisplay == NULL) + VBClFatalError(("Failed to open X11 display.\n")); + /* Message contents set to zero. */ + XClientMessageEvent clientMessage = { ClientMessage, 0, 0, 0, 0, 0, 8 }; + + if (XSendEvent(pDisplay, DefaultRootWindow(mDisplay), false, + PropertyChangeMask, (XEvent *)&clientMessage)) + rc = true; + XCloseDisplay(pDisplay); + LogRelFlowFunc(("returning %RTbool\n", rc)); + return rc; +} diff --git a/src/VBox/Additions/x11/VBoxClient/seamless-x11.h b/src/VBox/Additions/x11/VBoxClient/seamless-x11.h new file mode 100644 index 00000000..f7c0c268 --- /dev/null +++ b/src/VBox/Additions/x11/VBoxClient/seamless-x11.h @@ -0,0 +1,261 @@ +/* $Id: seamless-x11.h $ */ +/** @file + * + * Seamless mode: + * Linux guest. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#ifndef GA_INCLUDED_SRC_x11_VBoxClient_seamless_x11_h +#define GA_INCLUDED_SRC_x11_VBoxClient_seamless_x11_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <VBox/log.h> +#include <iprt/avl.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/extensions/shape.h> + +#define WM_TYPE_PROP "_NET_WM_WINDOW_TYPE" +#define WM_TYPE_DESKTOP_PROP "_NET_WM_WINDOW_TYPE_DESKTOP" + +/* This is defined wrong in my X11 header files! */ +#define VBoxShapeNotify 64 + +/** + * Callback which provides the interface for notifying the host of changes to + * the X11 window configuration, mainly split out from @a VBoxGuestSeamlessHost + * to simplify the unit test. + */ +typedef void FNSENDREGIONUPDATE(RTRECT *pRects, size_t cRects); +typedef FNSENDREGIONUPDATE *PFNSENDREGIONUPDATE; + +/** Structure containing information about a guest window's position and visible area. + Used inside of VBoxGuestWindowList. */ +struct VBoxGuestWinInfo { +public: + /** Header structure for insertion into an AVL tree */ + AVLU32NODECORE Core; + /** Is the window currently mapped? */ + bool mhasShape; + /** Co-ordinates in the guest screen. */ + int mX, mY; + /** Window dimensions. */ + int mWidth, mHeight; + /** Number of rectangles used to represent the visible area. */ + int mcRects; + /** Rectangles representing the visible area. These must be allocated + * by XMalloc and will be freed automatically if non-null when the class + * is destroyed. */ + XRectangle *mpRects; + /** Constructor. */ + VBoxGuestWinInfo(bool hasShape, int x, int y, int w, int h, int cRects, + XRectangle *pRects) + : mhasShape(hasShape), mX(x), mY(y), mWidth(w), mHeight(h), + mcRects(cRects), mpRects(pRects) {} + + /** Destructor */ + ~VBoxGuestWinInfo() + { + if (mpRects) + XFree(mpRects); + } + +private: + // We don't want a copy constructor or assignment operator + VBoxGuestWinInfo(const VBoxGuestWinInfo&); + VBoxGuestWinInfo& operator=(const VBoxGuestWinInfo&); +}; + +/** Callback type used for "DoWithAll" calls */ +typedef DECLCALLBACK(int) VBOXGUESTWINCALLBACK(VBoxGuestWinInfo *, void *); +/** Pointer to VBOXGUESTWINCALLBACK */ +typedef VBOXGUESTWINCALLBACK *PVBOXGUESTWINCALLBACK; + +DECLCALLBACK(int) inline VBoxGuestWinCleanup(VBoxGuestWinInfo *pInfo, void *) +{ + delete pInfo; + return VINF_SUCCESS; +} + +/** + * This class is just a wrapper around a map of structures containing + * information about the windows on the guest system. It has a function for + * adding a structure (see addWindow) and one for removing it by window + * handle (see removeWindow). + */ +class VBoxGuestWindowList +{ +private: + // We don't want a copy constructor or an assignment operator + VBoxGuestWindowList(const VBoxGuestWindowList&); + VBoxGuestWindowList& operator=(const VBoxGuestWindowList&); + + // Private class members + AVLU32TREE mWindows; + +public: + // Constructor + VBoxGuestWindowList(void) : mWindows(NULL) {} + // Destructor + ~VBoxGuestWindowList() + { + /** @todo having this inside the container class hard codes that the + * elements have to be allocated with the "new" operator, and + * I don't see a need to require this. */ + doWithAll(VBoxGuestWinCleanup, NULL); + } + + // Standard operations + VBoxGuestWinInfo *find(Window hWin) + { + return (VBoxGuestWinInfo *)RTAvlU32Get(&mWindows, hWin); + } + + void detachAll(PVBOXGUESTWINCALLBACK pCallback, void *pvParam) + { + RTAvlU32Destroy(&mWindows, (PAVLU32CALLBACK)pCallback, pvParam); + } + + int doWithAll(PVBOXGUESTWINCALLBACK pCallback, void *pvParam) + { + return RTAvlU32DoWithAll(&mWindows, 1, (PAVLU32CALLBACK)pCallback, + pvParam); + } + + bool addWindow(Window hWin, bool isMapped, int x, int y, int w, int h, int cRects, + XRectangle *pRects) + { + LogRelFlowFunc(("hWin=%lu, isMapped=%RTbool, x=%d, y=%d, w=%d, h=%d, cRects=%d\n", + (unsigned long) hWin, isMapped, x, y, w, h, cRects)); + VBoxGuestWinInfo *pInfo = new VBoxGuestWinInfo(isMapped, x, y, w, h, cRects, + pRects); + pInfo->Core.Key = hWin; + LogRelFlowFunc(("returning\n")); + return RTAvlU32Insert(&mWindows, &pInfo->Core); + } + + VBoxGuestWinInfo *removeWindow(Window hWin) + { + LogRelFlowFunc(("called\n")); + return (VBoxGuestWinInfo *)RTAvlU32Remove(&mWindows, hWin); + } +}; + +class SeamlessX11 +{ +private: + // We don't want a copy constructor or assignment operator + SeamlessX11(const SeamlessX11&); + SeamlessX11& operator=(const SeamlessX11&); + + // Private member variables + /** Pointer to the host callback. */ + PFNSENDREGIONUPDATE mHostCallback; + /** Our connection to the X11 display we are running on. */ + Display *mDisplay; + /** Class to keep track of visible guest windows. */ + VBoxGuestWindowList mGuestWindows; + /** The current set of seamless rectangles. */ + RTRECT *mpRects; + /** The current number of seamless rectangles. */ + int mcRects; + /** Do we support the X shaped window extension? */ + bool mSupportsShape; + /** Is seamless mode currently enabled? */ + bool mEnabled; + /** Have there been changes since the last time we sent a notification? */ + bool mChanged; + + // Private methods + + // Methods to manage guest window information + /** + * Store information about a desktop window and register for structure events on it. + * If it is mapped, go through the list of it's children and add information about + * mapped children to the tree of visible windows, making sure that those windows are + * not already in our list of desktop windows. + * + * @param hWin the window concerned - should be a "desktop" window + */ + void monitorClientList(void); + void unmonitorClientList(void); + void rebuildWindowTree(void); + void addClients(const Window hRoot); + bool isVirtualRoot(Window hWin); + void addClientWindow(Window hWin); + void freeWindowTree(void); + void updateHostSeamlessInfo(void); + int updateRects(void); + +public: + /** + * Initialise the guest and ensure that it is capable of handling seamless mode + * @param pHostCallback Host interface callback to notify of window configuration + * changes. + * + * @returns iprt status code + */ + int init(PFNSENDREGIONUPDATE pHostCallback); + + /** + * Shutdown seamless event monitoring. + */ + void uninit(void) + { + if (mHostCallback) + stop(); + mHostCallback = NULL; + if (mDisplay) + XCloseDisplay(mDisplay); + mDisplay = NULL; + } + + /** + * Initialise seamless event reporting in the guest. + * + * @returns IPRT status code + */ + int start(void); + /** Stop reporting seamless events. */ + void stop(void); + /** Get the current list of visible rectangles. */ + RTRECT *getRects(void); + /** Get the number of visible rectangles in the current list */ + size_t getRectCount(void); + + /** Process next event in the guest event queue - called by the event thread. */ + void nextConfigurationEvent(void); + /** Wake up the event thread if it is waiting for an event so that it can exit. */ + bool interruptEventWait(void); + + /* Methods to handle X11 events. These are public so that the unit test + * can call them. */ + void doConfigureEvent(Window hWin); + void doShapeEvent(Window hWin); + + SeamlessX11(void) + : mHostCallback(NULL), mDisplay(NULL), mpRects(NULL), mcRects(0), + mSupportsShape(false), mEnabled(false), mChanged(false) {} + + ~SeamlessX11() + { + uninit(); + } +}; + +#endif /* !GA_INCLUDED_SRC_x11_VBoxClient_seamless_x11_h */ diff --git a/src/VBox/Additions/x11/VBoxClient/seamless.cpp b/src/VBox/Additions/x11/VBoxClient/seamless.cpp new file mode 100644 index 00000000..62822bf7 --- /dev/null +++ b/src/VBox/Additions/x11/VBoxClient/seamless.cpp @@ -0,0 +1,344 @@ +/* $Id: seamless.cpp $ */ +/** @file + * X11 Guest client - seamless mode: main logic, communication with the host and + * wrapper interface for the main code of the VBoxClient deamon. The + * X11-specific parts are split out into their own file for ease of testing. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* Header files * +*********************************************************************************************************************************/ +#include <X11/Xlib.h> + +#include <VBox/log.h> +#include <VBox/VBoxGuestLib.h> +#include <iprt/errcore.h> +#include <iprt/mem.h> + +#include "VBoxClient.h" +#include "seamless.h" + +#include <new> + +SeamlessMain::SeamlessMain(void) +{ + LogRelFlowFunc(("\n")); + mX11MonitorThread = NIL_RTTHREAD; + mX11MonitorThreadStopping = false; + mMode = VMMDev_Seamless_Disabled; + mfPaused = true; +} + +SeamlessMain::~SeamlessMain() +{ + LogRelFlowFunc(("\n")); + stop(); +} + +/** + * Update the set of visible rectangles in the host. + */ +static void sendRegionUpdate(RTRECT *pRects, size_t cRects) +{ + LogRelFlowFunc(("\n")); + if (cRects && !pRects) /* Assertion */ + { + LogRelFunc(("ERROR: called with null pointer!\n")); + return; + } + VbglR3SeamlessSendRects(cRects, pRects); + LogRelFlowFunc(("returning\n")); +} + +/** + * initialise the service. + */ +int SeamlessMain::init(void) +{ + int rc; + const char *pcszStage; + + LogRelFlowFunc(("\n")); + do { + pcszStage = "Connecting to the X server"; + rc = mX11Monitor.init(sendRegionUpdate); + if (RT_FAILURE(rc)) + break; + pcszStage = "Setting guest IRQ filter mask"; + rc = VbglR3CtlFilterMask(VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST, 0); + if (RT_FAILURE(rc)) + break; + pcszStage = "Reporting support for seamless capability"; + rc = VbglR3SeamlessSetCap(true); + if (RT_FAILURE(rc)) + break; + rc = startX11MonitorThread(); + if (RT_FAILURE(rc)) + break; + } while(0); + if (RT_FAILURE(rc)) + VBClFatalError(("VBoxClient (seamless): failed to start. Stage: \"%s\" Error: %Rrc\n", + pcszStage, rc)); + return rc; +} + +/** + * Run the main service thread which listens for host state change + * notifications. + * @returns iprt status value. Service will be set to the stopped state on + * failure. + */ +int SeamlessMain::run(void) +{ + int rc = VINF_SUCCESS; + + LogRelFlowFunc(("\n")); + /* This will only exit if something goes wrong. */ + while (RT_SUCCESS(rc) || rc == VERR_INTERRUPTED) + { + if (RT_FAILURE(rc)) + /* If we are not stopping, sleep for a bit to avoid using up too + much CPU while retrying. */ + RTThreadYield(); + rc = nextStateChangeEvent(); + } + if (RT_FAILURE(rc)) + { + LogRel(("VBoxClient (seamless): event loop failed with error: %Rrc\n", + rc)); + stop(); + } + return rc; +} + +/** Stops the service. */ +void SeamlessMain::stop() +{ + LogRelFlowFunc(("\n")); + VbglR3SeamlessSetCap(false); + VbglR3CtlFilterMask(0, VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST); + stopX11MonitorThread(); + mX11Monitor.uninit(); + LogRelFlowFunc(("returning\n")); +} + +/** + * Waits for a seamless state change events from the host and dispatch it. + * + * @returns IRPT return code. + */ +int SeamlessMain::nextStateChangeEvent(void) +{ + VMMDevSeamlessMode newMode = VMMDev_Seamless_Disabled; + + LogRelFlowFunc(("\n")); + int rc = VbglR3SeamlessWaitEvent(&newMode); + if (RT_SUCCESS(rc)) + { + mMode = newMode; + switch (newMode) + { + case VMMDev_Seamless_Visible_Region: + /* A simplified seamless mode, obtained by making the host VM window + * borderless and making the guest desktop transparent. */ + LogRelFlowFunc(("\"Visible region\" mode requested (VBoxClient).\n")); + break; + case VMMDev_Seamless_Disabled: + LogRelFlowFunc(("\"Disabled\" mode requested (VBoxClient).\n")); + break; + case VMMDev_Seamless_Host_Window: + /* One host window represents one guest window. Not yet implemented. */ + LogRelFunc(("Unsupported \"host window\" mode requested (VBoxClient).\n")); + return VERR_NOT_SUPPORTED; + default: + LogRelFunc(("Unsupported mode %d requested (VBoxClient).\n", + newMode)); + return VERR_NOT_SUPPORTED; + } + } + if (RT_SUCCESS(rc) || rc == VERR_TRY_AGAIN) + { + if (mMode == VMMDev_Seamless_Visible_Region) + mfPaused = false; + else + mfPaused = true; + mX11Monitor.interruptEventWait(); + } + else + { + LogRelFunc(("VbglR3SeamlessWaitEvent returned %Rrc (VBoxClient)\n", rc)); + } + LogRelFlowFunc(("returning %Rrc\n", rc)); + return rc; +} + +/** + * The actual X11 window configuration change monitor thread function. + */ +int SeamlessMain::x11MonitorThread(RTTHREAD hThreadSelf, void *pvUser) +{ + RT_NOREF1(hThreadSelf); + SeamlessMain *pHost = (SeamlessMain *)pvUser; + int rc = VINF_SUCCESS; + + LogRelFlowFunc(("\n")); + while (!pHost->mX11MonitorThreadStopping) + { + if (!pHost->mfPaused) + { + rc = pHost->mX11Monitor.start(); + if (RT_FAILURE(rc)) + VBClFatalError(("Failed to change the X11 seamless service state, mfPaused=%RTbool, rc=%Rrc\n", + pHost->mfPaused, rc)); + } + pHost->mX11Monitor.nextConfigurationEvent(); + if (pHost->mfPaused || pHost->mX11MonitorThreadStopping) + pHost->mX11Monitor.stop(); + } + LogRelFlowFunc(("returning %Rrc\n", rc)); + return rc; +} + +/** + * Start the X11 window configuration change monitor thread. + */ +int SeamlessMain::startX11MonitorThread(void) +{ + int rc; + + mX11MonitorThreadStopping = false; + if (isX11MonitorThreadRunning()) + return VINF_SUCCESS; + rc = RTThreadCreate(&mX11MonitorThread, x11MonitorThread, this, 0, + RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE, + "X11 events"); + if (RT_FAILURE(rc)) + LogRelFunc(("Warning: failed to start X11 monitor thread (VBoxClient).\n")); + return rc; +} + +/** + * Send a signal to the thread function that it should exit + */ +int SeamlessMain::stopX11MonitorThread(void) +{ + int rc; + + mX11MonitorThreadStopping = true; + if (!isX11MonitorThreadRunning()) + return VINF_SUCCESS; + mX11Monitor.interruptEventWait(); + rc = RTThreadWait(mX11MonitorThread, RT_INDEFINITE_WAIT, NULL); + if (RT_SUCCESS(rc)) + mX11MonitorThread = NIL_RTTHREAD; + else + LogRelThisFunc(("Failed to stop X11 monitor thread, rc=%Rrc!\n", + rc)); + return rc; +} + +/** Service magic number, start of a UUID. */ +#define SEAMLESSSERVICE_MAGIC 0xd28ba727 + +/** VBoxClient service class wrapping the logic for the seamless service while + * the main VBoxClient code provides the daemon logic needed by all services. + */ +struct SEAMLESSSERVICE +{ + /** The service interface. */ + struct VBCLSERVICE *pInterface; + /** Magic number for sanity checks. */ + uint32_t magic; + /** Seamless service object. */ + SeamlessMain mSeamless; + /** Are we initialised yet? */ + bool mIsInitialised; +}; + +static const char *getPidFilePath(void) +{ + return ".vboxclient-seamless.pid"; +} + +static struct SEAMLESSSERVICE *getClassFromInterface(struct VBCLSERVICE ** + ppInterface) +{ + struct SEAMLESSSERVICE *pSelf = (struct SEAMLESSSERVICE *)ppInterface; + if (pSelf->magic != SEAMLESSSERVICE_MAGIC) + VBClFatalError(("Bad seamless service object!\n")); + return pSelf; +} + +static int init(struct VBCLSERVICE **ppInterface) +{ + struct SEAMLESSSERVICE *pSelf = getClassFromInterface(ppInterface); + int rc; + + if (pSelf->mIsInitialised) + return VERR_INTERNAL_ERROR; + /* Initialise the guest library. */ + rc = VbglR3InitUser(); + if (RT_FAILURE(rc)) + VBClFatalError(("Failed to connect to the VirtualBox kernel service, rc=%Rrc\n", rc)); + rc = pSelf->mSeamless.init(); + if (RT_FAILURE(rc)) + return rc; + pSelf->mIsInitialised = true; + return VINF_SUCCESS; +} + +static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised) +{ + RT_NOREF1(fDaemonised); + struct SEAMLESSSERVICE *pSelf = getClassFromInterface(ppInterface); + int rc; + + if (!pSelf->mIsInitialised) + return VERR_INTERNAL_ERROR; + /* This only exits on error. */ + rc = pSelf->mSeamless.run(); + pSelf->mIsInitialised = false; + return rc; +} + +static void cleanup(struct VBCLSERVICE **ppInterface) +{ + NOREF(ppInterface); + VbglR3SeamlessSetCap(false); + VbglR3Term(); +} + +struct VBCLSERVICE vbclSeamlessInterface = +{ + getPidFilePath, + init, + run, + cleanup +}; + +struct VBCLSERVICE **VBClGetSeamlessService() +{ + struct SEAMLESSSERVICE *pService = + (struct SEAMLESSSERVICE *)RTMemAlloc(sizeof(*pService)); + + if (!pService) + VBClFatalError(("Out of memory\n")); + pService->pInterface = &vbclSeamlessInterface; + pService->magic = SEAMLESSSERVICE_MAGIC; + new(&pService->mSeamless) SeamlessMain(); + pService->mIsInitialised = false; + return &pService->pInterface; +} diff --git a/src/VBox/Additions/x11/VBoxClient/seamless.h b/src/VBox/Additions/x11/VBoxClient/seamless.h new file mode 100644 index 00000000..478ab58c --- /dev/null +++ b/src/VBox/Additions/x11/VBoxClient/seamless.h @@ -0,0 +1,113 @@ +/* $Id: seamless.h $ */ +/** @file + * X11 Guest client - seamless mode, missing proper description while using the + * potentially confusing word 'host'. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#ifndef GA_INCLUDED_SRC_x11_VBoxClient_seamless_h +#define GA_INCLUDED_SRC_x11_VBoxClient_seamless_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <iprt/thread.h> + +#include <VBox/log.h> +#include <VBox/VBoxGuestLib.h> /* for the R3 guest library functions */ + +#include "seamless-x11.h" + +/** + * Interface to the host + */ +class SeamlessMain +{ +private: + // We don't want a copy constructor or assignment operator + SeamlessMain(const SeamlessMain&); + SeamlessMain& operator=(const SeamlessMain&); + + /** X11 event monitor object */ + SeamlessX11 mX11Monitor; + + /** Thread to start and stop when we enter and leave seamless mode which + * monitors X11 windows in the guest. */ + RTTHREAD mX11MonitorThread; + /** Should the X11 monitor thread be stopping? */ + volatile bool mX11MonitorThreadStopping; + + /** The current seamless mode we are in. */ + VMMDevSeamlessMode mMode; + /** Is the service currently paused? */ + volatile bool mfPaused; + + /** + * Waits for a seamless state change events from the host and dispatch it. This is + * meant to be called by the host event monitor thread exclusively. + * + * @returns IRPT return code. + */ + int nextStateChangeEvent(void); + + /** Thread function to monitor X11 window configuration changes. */ + static DECLCALLBACK(int) x11MonitorThread(RTTHREAD self, void *pvUser); + + /** Helper to start the X11 monitor thread. */ + int startX11MonitorThread(void); + + /** Helper to stop the X11 monitor thread again. */ + int stopX11MonitorThread(void); + + /** Is the service currently actively monitoring X11 windows? */ + bool isX11MonitorThreadRunning() + { + return mX11MonitorThread != NIL_RTTHREAD; + } + +public: + SeamlessMain(void); + virtual ~SeamlessMain(); + + /** + * Initialise the service. + */ + int init(void); + + /** + * Run the service. + * @returns iprt status value + */ + int run(void); + + /** + * Stops the service. + */ + void stop(); + + /** Pause the service loop. This must be safe to call on a different thread + * and potentially before @a run is or after it exits. + * This is called by the VT monitoring thread to allow the service to disable + * itself when the X server is switched out. If the monitoring functionality + * is available then @a pause or @a resume will be called as soon as it starts + * up. */ + int pause(); + /** Resume after pausing. The same applies here as for @a pause. */ + int resume(); + + /** Run a few tests to be sure everything is working as intended. */ + int selfTest(); +}; + +#endif /* !GA_INCLUDED_SRC_x11_VBoxClient_seamless_h */ diff --git a/src/VBox/Additions/x11/VBoxClient/testcase/tstSeamlessX11-auto.cpp b/src/VBox/Additions/x11/VBoxClient/testcase/tstSeamlessX11-auto.cpp new file mode 100644 index 00000000..b18f1fd9 --- /dev/null +++ b/src/VBox/Additions/x11/VBoxClient/testcase/tstSeamlessX11-auto.cpp @@ -0,0 +1,738 @@ +/* $Id: tstSeamlessX11-auto.cpp $ */ +/** @file + * Automated test of the X11 seamless Additions code. + * @todo Better separate test data from implementation details! + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <stdlib.h> /* exit() */ + +#include <X11/Xatom.h> +#include <X11/Xmu/WinUtil.h> + +#include <iprt/initterm.h> +#include <iprt/mem.h> +#include <iprt/path.h> +#include <iprt/semaphore.h> +#include <iprt/stream.h> +#include <iprt/string.h> + +#include "../seamless.h" + +#undef DefaultRootWindow + +/****************************************************** +* Mock X11 functions needed by the seamless X11 class * +******************************************************/ + +int XFree(void *data) +{ + RTMemFree(data); + return 0; +} + +#define TEST_DISPLAY ((Display *)0xffff) +#define TEST_ROOT ((Window)1) + +extern void vbclFatalError(char *psz) +{ + RTPrintf("Fatal error: %s\n", psz); + exit(1); +} + +extern "C" Display *XOpenDisplay(const char *display_name); +Display *XOpenDisplay(const char *display_name) +{ + RT_NOREF1(display_name); + return TEST_DISPLAY; +} + +extern "C" int XCloseDisplay(Display *display); +int XCloseDisplay(Display *display) +{ + RT_NOREF1(display); + Assert(display == TEST_DISPLAY); + return 0; +} + +enum +{ + ATOM_PROP = 1, + ATOM_DESKTOP_PROP +}; + +extern "C" Atom XInternAtom(Display *display, const char *atom_name, Bool only_if_exists); +Atom XInternAtom(Display *display, const char *atom_name, Bool only_if_exists) +{ + RT_NOREF2(only_if_exists, display); + Assert(display == TEST_DISPLAY); + if (!RTStrCmp(atom_name, WM_TYPE_PROP)) + return (Atom) ATOM_PROP; + if (!RTStrCmp(atom_name, WM_TYPE_DESKTOP_PROP)) + return (Atom) ATOM_DESKTOP_PROP; + AssertFailed(); + return (Atom)0; +} + +/** The window (if any) on which the WM_TYPE_PROP property is set to the + * WM_TYPE_DESKTOP_PROP atom. */ +static Window g_hSmlsDesktopWindow = 0; + +extern "C" int XGetWindowProperty(Display *display, Window w, Atom property, + long long_offset, long long_length, + Bool delProp, Atom req_type, + Atom *actual_type_return, + int *actual_format_return, + unsigned long *nitems_return, + unsigned long *bytes_after_return, + unsigned char **prop_return); +int XGetWindowProperty(Display *display, Window w, Atom property, + long long_offset, long long_length, Bool delProp, + Atom req_type, Atom *actual_type_return, + int *actual_format_return, + unsigned long *nitems_return, + unsigned long *bytes_after_return, + unsigned char **prop_return) +{ + RT_NOREF2(display, long_length); + Assert(display == TEST_DISPLAY); + Atom atomType = XInternAtom (display, WM_TYPE_PROP, true); + Atom atomTypeDesktop = XInternAtom (display, WM_TYPE_DESKTOP_PROP, true); + /* We only handle things we expect. */ + AssertReturn((req_type == XA_ATOM) || (req_type == AnyPropertyType), + 0xffff); + AssertReturn(property == atomType, 0xffff); + *actual_type_return = XA_ATOM; + *actual_format_return = sizeof(Atom) * 8; + *nitems_return = 0; + *bytes_after_return = sizeof(Atom); + *prop_return = NULL; + if ((w != g_hSmlsDesktopWindow) || (g_hSmlsDesktopWindow == 0)) + return Success; + AssertReturn(long_offset == 0, 0); + AssertReturn(delProp == false, 0); + unsigned char *pProp; + pProp = (unsigned char *)RTMemDup(&atomTypeDesktop, + sizeof(atomTypeDesktop)); + AssertReturn(pProp, 0xffff); + *nitems_return = 1; + *prop_return = pProp; + *bytes_after_return = 0; + return 0; +} + +#if 0 /* unused */ +/** Sets the current set of properties for all mock X11 windows */ +static void smlsSetDesktopWindow(Window hWin) +{ + g_hSmlsDesktopWindow = hWin; +} +#endif + +extern "C" Bool XShapeQueryExtension(Display *dpy, int *event_basep, int *error_basep); +Bool XShapeQueryExtension(Display *dpy, int *event_basep, int *error_basep) +{ + RT_NOREF3(dpy, event_basep, error_basep); + Assert(dpy == TEST_DISPLAY); + return true; +} + +/* We silently ignore this for now. */ +extern "C" int XSelectInput(Display *display, Window w, long event_mask); +int XSelectInput(Display *display, Window w, long event_mask) +{ + RT_NOREF3(display, w, event_mask); + Assert(display == TEST_DISPLAY); + return 0; +} + +/* We silently ignore this for now. */ +extern "C" void XShapeSelectInput(Display *display, Window w, unsigned long event_mask); +void XShapeSelectInput(Display *display, Window w, unsigned long event_mask) +{ + RT_NOREF3(display, w, event_mask); + Assert(display == TEST_DISPLAY); +} + +extern "C" Window XDefaultRootWindow(Display *display); +Window XDefaultRootWindow(Display *display) +{ + RT_NOREF1(display); + Assert(display == TEST_DISPLAY); + return TEST_ROOT; +} + +static unsigned g_cSmlsWindows = 0; +static Window *g_paSmlsWindows = NULL; +static XWindowAttributes *g_paSmlsWinAttribs = NULL; +static const char **g_papszSmlsWinNames = NULL; + +extern "C" Status XQueryTree(Display *display, Window w, Window *root_return, + Window *parent_return, Window **children_return, + unsigned int *nchildren_return); +Status XQueryTree(Display *display, Window w, Window *root_return, + Window *parent_return, Window **children_return, + unsigned int *nchildren_return) +{ + RT_NOREF1(display); + Assert(display == TEST_DISPLAY); + AssertReturn(w == TEST_ROOT, False); /* We support nothing else */ + AssertPtrReturn(children_return, False); + AssertReturn(g_paSmlsWindows, False); + if (root_return) + *root_return = TEST_ROOT; + if (parent_return) + *parent_return = TEST_ROOT; + *children_return = (Window *)RTMemDup(g_paSmlsWindows, + g_cSmlsWindows * sizeof(Window)); + if (nchildren_return) + *nchildren_return = g_cSmlsWindows; + return (g_cSmlsWindows != 0); +} + +extern "C" Window XmuClientWindow(Display *dpy, Window win); +Window XmuClientWindow(Display *dpy, Window win) +{ + RT_NOREF1(dpy); + Assert(dpy == TEST_DISPLAY); + return win; +} + +extern "C" Status XGetWindowAttributes(Display *display, Window w, + XWindowAttributes *window_attributes_return); +Status XGetWindowAttributes(Display *display, Window w, + XWindowAttributes *window_attributes_return) +{ + RT_NOREF1(display); + Assert(display == TEST_DISPLAY); + AssertPtrReturn(window_attributes_return, 1); + for (unsigned i = 0; i < g_cSmlsWindows; ++i) + if (g_paSmlsWindows[i] == w) + { + *window_attributes_return = g_paSmlsWinAttribs[i]; + return 1; + } + return 0; +} + +extern "C" Status XGetWMNormalHints(Display *display, Window w, + XSizeHints *hints_return, + long *supplied_return); + +Status XGetWMNormalHints(Display *display, Window w, + XSizeHints *hints_return, long *supplied_return) +{ + RT_NOREF4(display, w, hints_return, supplied_return); + Assert(display == TEST_DISPLAY); + return 1; +} + +static void smlsSetWindowAttributes(XWindowAttributes *pAttribs, + Window *pWindows, unsigned cAttribs, + const char **paNames) +{ + g_paSmlsWinAttribs = pAttribs; + g_paSmlsWindows = pWindows; + g_cSmlsWindows = cAttribs; + g_papszSmlsWinNames = paNames; +} + +static Window g_SmlsShapedWindow = 0; +static int g_cSmlsShapeRectangles = 0; +static XRectangle *g_pSmlsShapeRectangles = NULL; + +extern "C" XRectangle *XShapeGetRectangles (Display *dpy, Window window, + int kind, int *count, + int *ordering); +XRectangle *XShapeGetRectangles (Display *dpy, Window window, int kind, + int *count, int *ordering) +{ + RT_NOREF2(dpy, kind); + Assert(dpy == TEST_DISPLAY); + if ((window != g_SmlsShapedWindow) || (window == 0)) + return NULL; /* Probably not correct, but works for us. */ + *count = g_cSmlsShapeRectangles; + *ordering = 0; + return (XRectangle *)RTMemDup(g_pSmlsShapeRectangles, + sizeof(XRectangle) + * g_cSmlsShapeRectangles); +} + +static void smlsSetShapeRectangles(Window window, int cRects, + XRectangle *pRects) +{ + g_SmlsShapedWindow = window; + g_cSmlsShapeRectangles = cRects; + g_pSmlsShapeRectangles = pRects; +} + +static int g_SmlsEventType = 0; +static Window g_SmlsEventWindow = 0; + +/* This should not be needed in the bits of the code we test. */ +extern "C" int XNextEvent(Display *display, XEvent *event_return); +int XNextEvent(Display *display, XEvent *event_return) +{ + RT_NOREF1(display); + Assert(display == TEST_DISPLAY); + event_return->xany.type = g_SmlsEventType; + event_return->xany.window = g_SmlsEventWindow; + event_return->xmap.window = g_SmlsEventWindow; + return True; +} + +static void smlsSetNextEvent(int type, Window window) +{ + g_SmlsEventType = type; + g_SmlsEventWindow = window; +} + +/* This should not be needed in the bits of the code we test. */ +extern "C" Status XSendEvent(Display *display, Window w, Bool propagate, + long event_mask, XEvent *event_send); +Status XSendEvent(Display *display, Window w, Bool propagate, + long event_mask, XEvent *event_send) +{ + RT_NOREF5(display, w, propagate, event_mask, event_send); + Assert(display == TEST_DISPLAY); + AssertFailedReturn(0); +} + +/* This should not be needed in the bits of the code we test. */ +extern "C" int XFlush(Display *display); +int XFlush(Display *display) +{ + RT_NOREF1(display); + Assert(display == TEST_DISPLAY); + AssertFailedReturn(0); +} + +/** Global "received a notification" flag. */ +static bool g_fNotified = false; + +/** Dummy host call-back. */ +static void sendRegionUpdate(RTRECT *pRects, size_t cRects) +{ + RT_NOREF2(pRects, cRects); + g_fNotified = true; +} + +static bool gotNotification(void) +{ + if (!g_fNotified) + return false; + g_fNotified = false; + return true; +} + +/***************************** +* The actual tests to be run * +*****************************/ + +/** The name of the unit test */ +static const char *g_pszTestName = NULL; + +/*** Test fixture data and data structures ***/ + +/** A structure describing a test fixture to be run through. Each fixture + * describes the state of the windows visible (and unmapped) on the X server + * before and after a particular event is delivered, and the expected + * on-screen positions of all interesting visible windows at the end of the + * fixture as reported by the code (currently in the order it is likely to + * report them in, @todo sort this). We expect that the set of visible + * windows will be the same whether we start the code before the event and + * handle it or start the code after the event. + */ +struct SMLSFIXTURE +{ + /** The number of windows visible before the event */ + unsigned cWindowsBefore; + /** An array of Window IDs for the visible and unmapped windows before + * the event */ + Window *pahWindowsBefore; + /** The window attributes matching the windows in @a paWindowsBefore */ + XWindowAttributes *paAttribsBefore; + /** The window names matching the windows in @a paWindowsBefore */ + const char **papszNamesBefore; + /** The shaped window before the event - we allow at most one of these. + * Zero for none. */ + Window hShapeWindowBefore; + /** The number of rectangles in the shaped window before the event. */ + int cShapeRectsBefore; + /** The rectangles in the shaped window before the event */ + XRectangle *paShapeRectsBefore; + /** The number of windows visible after the event */ + unsigned cWindowsAfter; + /** An array of Window IDs for the visible and unmapped windows after + * the event */ + Window *pahWindowsAfter; + /** The window attributes matching the windows in @a paWindowsAfter */ + XWindowAttributes *paAttribsAfter; + /** The window names matching the windows in @a paWindowsAfter */ + const char **papszNamesAfter; + /** The shaped window after the event - we allow at most one of these. + * Zero for none. */ + Window hShapeWindowAfter; + /** The number of rectangles in the shaped window after the event. */ + int cShapeRectsAfter; + /** The rectangles in the shaped window after the event */ + XRectangle *paShapeRectsAfter; + /** The event to delivered */ + int x11EventType; + /** The window for which the event in @enmEvent is delivered */ + Window hEventWindow; + /** The number of windows expected to be reported at the end of the + * fixture */ + unsigned cReportedRects; + /** The onscreen positions of those windows. */ + RTRECT *paReportedRects; + /** Do we expect notification after the event? */ + bool fExpectNotification; +}; + +/*** Test fixture to test the code against X11 configure (move) events ***/ + +static Window g_ahWin1[] = { 20 }; +static XWindowAttributes g_aAttrib1Before[] = +{ { 100, 200, 200, 300, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, IsViewable } +}; +static XRectangle g_aRectangle1[] = +{ + { 0, 0, 50, 50 }, + { 50, 50, 150, 250 } +}; +static XWindowAttributes g_aAttrib1After[] = +{ { 200, 300, 200, 300, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, IsViewable } +}; +static const char *g_apszNames1[] = { "Test Window" }; + +AssertCompile(RT_ELEMENTS(g_ahWin1) == RT_ELEMENTS(g_aAttrib1Before)); +AssertCompile(RT_ELEMENTS(g_ahWin1) == RT_ELEMENTS(g_aAttrib1After)); +AssertCompile(RT_ELEMENTS(g_ahWin1) == RT_ELEMENTS(g_apszNames1)); + +static RTRECT g_aRects1[] = +{ + { 200, 300, 250, 350 }, + { 250, 350, 400, 600 } +}; + +static SMLSFIXTURE g_testMove = +{ + RT_ELEMENTS(g_ahWin1), + g_ahWin1, + g_aAttrib1Before, + g_apszNames1, + 20, + RT_ELEMENTS(g_aRectangle1), + g_aRectangle1, + RT_ELEMENTS(g_ahWin1), + g_ahWin1, + g_aAttrib1After, + g_apszNames1, + 20, + RT_ELEMENTS(g_aRectangle1), + g_aRectangle1, + ConfigureNotify, + 20, + RT_ELEMENTS(g_aRects1), + g_aRects1, + true +}; + +/*** Test fixture to test the code against X11 configure (resize) events ***/ + +static XWindowAttributes g_aAttrib2Before[] = +{ { 100, 200, 200, 300, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, IsViewable } +}; +static XRectangle g_aRectangle2Before[] = +{ + { 0, 0, 50, 50 }, + { 50, 50, 100, 100 } +}; + +AssertCompile(RT_ELEMENTS(g_ahWin1) == RT_ELEMENTS(g_aAttrib2Before)); + +static SMLSFIXTURE g_testResize = +{ + RT_ELEMENTS(g_ahWin1), + g_ahWin1, + g_aAttrib2Before, + g_apszNames1, + 20, + RT_ELEMENTS(g_aRectangle2Before), + g_aRectangle2Before, + RT_ELEMENTS(g_ahWin1), + g_ahWin1, + g_aAttrib1After, + g_apszNames1, + 20, + RT_ELEMENTS(g_aRectangle1), + g_aRectangle1, + ConfigureNotify, + 20, + RT_ELEMENTS(g_aRects1), + g_aRects1, + true +}; + +/*** Test fixture to test the code against X11 map events ***/ + +static XWindowAttributes g_aAttrib3Before[] = +{ { 200, 300, 200, 300, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, IsUnmapped } +}; + +AssertCompile(RT_ELEMENTS(g_ahWin1) == RT_ELEMENTS(g_aAttrib3Before)); + +static SMLSFIXTURE g_testMap = +{ + RT_ELEMENTS(g_ahWin1), + g_ahWin1, + g_aAttrib3Before, + g_apszNames1, + 20, + RT_ELEMENTS(g_aRectangle1), + g_aRectangle1, + RT_ELEMENTS(g_ahWin1), + g_ahWin1, + g_aAttrib1After, + g_apszNames1, + 20, + RT_ELEMENTS(g_aRectangle1), + g_aRectangle1, + MapNotify, + 20, + RT_ELEMENTS(g_aRects1), + g_aRects1, + true +}; + +/*** Test fixtures to test the code against X11 unmap events ***/ + +static XWindowAttributes g_aAttrib4After[] = +{ { 100, 200, 300, 400, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, IsUnmapped } +}; + +AssertCompile(RT_ELEMENTS(g_ahWin1) == RT_ELEMENTS(g_aAttrib4After)); + +static SMLSFIXTURE g_testUnmap = +{ + RT_ELEMENTS(g_ahWin1), + g_ahWin1, + g_aAttrib1Before, + g_apszNames1, + 20, + RT_ELEMENTS(g_aRectangle1), + g_aRectangle1, + RT_ELEMENTS(g_ahWin1), + g_ahWin1, + g_aAttrib4After, + g_apszNames1, + 20, + RT_ELEMENTS(g_aRectangle1), + g_aRectangle1, + UnmapNotify, + 20, + 0, + NULL, + true +}; + +/*** A window we are not monitoring has been unmapped. Nothing should + *** happen, especially nothing bad. ***/ + +static RTRECT g_aRects2[] = +{ + { 100, 200, 150, 250 }, + { 150, 250, 300, 500 } +}; + +static SMLSFIXTURE g_testUnmapOther = +{ + RT_ELEMENTS(g_ahWin1), + g_ahWin1, + g_aAttrib1Before, + g_apszNames1, + 20, + RT_ELEMENTS(g_aRectangle1), + g_aRectangle1, + RT_ELEMENTS(g_ahWin1), + g_ahWin1, + g_aAttrib1Before, + g_apszNames1, + 20, + RT_ELEMENTS(g_aRectangle1), + g_aRectangle1, + UnmapNotify, + 21, + RT_ELEMENTS(g_aRects2), + g_aRects2, + false +}; + +/*** Test fixture to test the code against X11 shape events ***/ + +static XRectangle g_aRectangle5Before[] = +{ + { 0, 0, 200, 200 } +}; + +static SMLSFIXTURE g_testShape = +{ + RT_ELEMENTS(g_ahWin1), + g_ahWin1, + g_aAttrib1After, + g_apszNames1, + 20, + RT_ELEMENTS(g_aRectangle5Before), + g_aRectangle5Before, + RT_ELEMENTS(g_ahWin1), + g_ahWin1, + g_aAttrib1After, + g_apszNames1, + 20, + RT_ELEMENTS(g_aRectangle1), + g_aRectangle1, + VBoxShapeNotify, + 20, + RT_ELEMENTS(g_aRects1), + g_aRects1, + true +}; + +/*** And the test code proper ***/ + +/** Compare two RTRECT structures */ +static bool smlsCompRect(RTRECT *pFirst, RTRECT *pSecond) +{ + return ( (pFirst->xLeft == pSecond->xLeft) + && (pFirst->yTop == pSecond->yTop) + && (pFirst->xRight == pSecond->xRight) + && (pFirst->yBottom == pSecond->yBottom)); +} + +static void smlsPrintDiffRects(RTRECT *pExp, RTRECT *pGot) +{ + RTPrintf(" Expected: %d, %d, %d, %d. Got: %d, %d, %d, %d\n", + pExp->xLeft, pExp->yTop, pExp->xRight, pExp->yBottom, + pGot->xLeft, pGot->yTop, pGot->xRight, pGot->yBottom); +} + +/** Run through a test fixture */ +static unsigned smlsDoFixture(SMLSFIXTURE *pFixture, const char *pszDesc) +{ + SeamlessX11 subject; + unsigned cErrs = 0; + + subject.init(sendRegionUpdate); + smlsSetWindowAttributes(pFixture->paAttribsBefore, + pFixture->pahWindowsBefore, + pFixture->cWindowsBefore, + pFixture->papszNamesBefore); + smlsSetShapeRectangles(pFixture->hShapeWindowBefore, + pFixture->cShapeRectsBefore, + pFixture->paShapeRectsBefore); + subject.start(); + smlsSetWindowAttributes(pFixture->paAttribsAfter, + pFixture->pahWindowsAfter, + pFixture->cWindowsAfter, + pFixture->papszNamesAfter); + smlsSetShapeRectangles(pFixture->hShapeWindowAfter, + pFixture->cShapeRectsAfter, + pFixture->paShapeRectsAfter); + smlsSetNextEvent(pFixture->x11EventType, pFixture->hEventWindow); + if (gotNotification()) /* Initial window tree rebuild */ + { + RTPrintf("%s: fixture: %s. Notification was set before the first event!!!\n", + g_pszTestName, pszDesc); + ++cErrs; + } + subject.nextConfigurationEvent(); + if (!gotNotification()) + { + RTPrintf("%s: fixture: %s. No notification was sent for the initial window tree rebuild.\n", + g_pszTestName, pszDesc); + ++cErrs; + } + smlsSetNextEvent(0, 0); + subject.nextConfigurationEvent(); + if (pFixture->fExpectNotification && !gotNotification()) + { + RTPrintf("%s: fixture: %s. No notification was sent after the event.\n", + g_pszTestName, pszDesc); + ++cErrs; + } + RTRECT *pRects = subject.getRects(); + size_t cRects = subject.getRectCount(); + if (cRects != pFixture->cReportedRects) + { + RTPrintf("%s: fixture: %s. Wrong number of rectangles reported after processing event (expected %u, got %u).\n", + g_pszTestName, pszDesc, pFixture->cReportedRects, + cRects); + ++cErrs; + } + else + for (unsigned i = 0; i < cRects; ++i) + if (!smlsCompRect(&pRects[i], &pFixture->paReportedRects[i])) + { + RTPrintf("%s: fixture: %s. Rectangle %u wrong after processing event.\n", + g_pszTestName, pszDesc, i); + smlsPrintDiffRects(&pFixture->paReportedRects[i], + &pRects[i]); + ++cErrs; + break; + } + subject.stop(); + subject.start(); + if (cRects != pFixture->cReportedRects) + { + RTPrintf("%s: fixture: %s. Wrong number of rectangles reported without processing event (expected %u, got %u).\n", + g_pszTestName, pszDesc, pFixture->cReportedRects, + cRects); + ++cErrs; + } + else + for (unsigned i = 0; i < cRects; ++i) + if (!smlsCompRect(&pRects[i], &pFixture->paReportedRects[i])) + { + RTPrintf("%s: fixture: %s. Rectangle %u wrong without processing event.\n", + g_pszTestName, pszDesc, i); + smlsPrintDiffRects(&pFixture->paReportedRects[i], + &pRects[i]); + ++cErrs; + break; + } + return cErrs; +} + +int main( int argc, char **argv) +{ + RTR3InitExe(argc, &argv, 0); + unsigned cErrs = 0; + g_pszTestName = RTPathFilename(argv[0]); + + RTPrintf("%s: TESTING\n", g_pszTestName); + cErrs += smlsDoFixture(&g_testMove, + "ConfigureNotify event (window moved)"); + // Currently not working + cErrs += smlsDoFixture(&g_testResize, + "ConfigureNotify event (window resized)"); + cErrs += smlsDoFixture(&g_testMap, "MapNotify event"); + cErrs += smlsDoFixture(&g_testUnmap, "UnmapNotify event"); + cErrs += smlsDoFixture(&g_testUnmapOther, + "UnmapNotify event for unmonitored window"); + cErrs += smlsDoFixture(&g_testShape, "ShapeNotify event"); + if (cErrs > 0) + RTPrintf("%u errors\n", cErrs); + return cErrs == 0 ? 0 : 1; +} diff --git a/src/VBox/Additions/x11/VBoxClient/testcase/tstSeamlessX11.cpp b/src/VBox/Additions/x11/VBoxClient/testcase/tstSeamlessX11.cpp new file mode 100644 index 00000000..07916cec --- /dev/null +++ b/src/VBox/Additions/x11/VBoxClient/testcase/tstSeamlessX11.cpp @@ -0,0 +1,139 @@ +/* $Id: tstSeamlessX11.cpp $ */ +/** @file + * Linux seamless guest additions simulator in host. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <stdlib.h> /* exit() */ + +#include <iprt/errcore.h> +#include <iprt/initterm.h> +#include <iprt/semaphore.h> +#include <iprt/stream.h> +#include <VBox/VBoxGuestLib.h> + +#include "../seamless.h" + +static RTSEMEVENT eventSem; + +/** Exit with a fatal error. */ +void vbclFatalError(char *pszMessage) +{ + RTPrintf("Fatal error: %s", pszMessage); + exit(1); +} + +int VBClStartVTMonitor() +{ + return VINF_SUCCESS; +} + +int VbglR3SeamlessSendRects(uint32_t cRects, PRTRECT pRects) +{ + RTPrintf("Received rectangle update (%u rectangles):\n", cRects); + for (unsigned i = 0; i < cRects; ++i) + { + RTPrintf(" xLeft: %d yTop: %d xRight: %d yBottom: %d\n", + pRects[i].xLeft, pRects[i].yTop, pRects[i].xRight, + pRects[i].yBottom); + } + return VINF_SUCCESS; +} + +int VbglR3SeamlessSetCap(bool bState) +{ + RTPrintf("%s\n", bState ? "Seamless capability set" + : "Seamless capability unset"); + return VINF_SUCCESS; +} + +int VbglR3CtlFilterMask(uint32_t u32OrMask, uint32_t u32NotMask) +{ + RTPrintf("IRQ filter mask changed. Or mask: 0x%x. Not mask: 0x%x\n", + u32OrMask, u32NotMask); + return VINF_SUCCESS; +} + +int VbglR3SeamlessWaitEvent(VMMDevSeamlessMode *pMode) +{ + static bool active = false; + + int rc = VINF_SUCCESS; + if (!active) + { + active = true; + *pMode = VMMDev_Seamless_Visible_Region; + } + else + rc = RTSemEventWait(eventSem, RT_INDEFINITE_WAIT); + return rc; +} + +VBGLR3DECL(int) VbglR3InitUser(void) { return VINF_SUCCESS; } +VBGLR3DECL(void) VbglR3Term(void) {} + +/** + * Xlib error handler for certain errors that we can't avoid. + */ +int vboxClientXLibErrorHandler(Display *pDisplay, XErrorEvent *pError) +{ + char errorText[1024]; + + if (pError->error_code == BadWindow) + { + /* This can be triggered if a guest application destroys a window before we notice. */ + RTPrintf("ignoring BadAtom error and returning\n"); + return 0; + } + XGetErrorText(pDisplay, pError->error_code, errorText, sizeof(errorText)); + RTPrintf("An X Window protocol error occurred: %s\n" + " Request code: %d\n" + " Minor code: %d\n" + " Serial number of the failed request: %d\n\n" + "exiting.\n", + errorText, (int)pError->request_code, (int)pError->minor_code, + (int)pError->serial); + exit(1); +} + +int main( int argc, char **argv) +{ + int rc = VINF_SUCCESS; + + RTR3InitExe(argc, &argv, 0); + RTPrintf("VirtualBox guest additions X11 seamless mode testcase\n"); + if (0 == XInitThreads()) + { + RTPrintf("Failed to initialise X11 threading, exiting.\n"); + exit(1); + } + /* Set an X11 error handler, so that we don't die when we get unavoidable errors. */ + XSetErrorHandler(vboxClientXLibErrorHandler); + RTPrintf("\nType Ctrl-C to exit...\n"); + RTSemEventCreate(&eventSem); + /** Our instance of the seamless class. */ + SeamlessMain seamless; + LogRel(("Starting seamless Guest Additions...\n")); + rc = seamless.init(); + if (rc != VINF_SUCCESS) + { + RTPrintf("Failed to initialise seamless Additions, rc = %Rrc\n", rc); + } + rc = seamless.run(); + if (rc != VINF_SUCCESS) + { + RTPrintf("Failed to run seamless Additions, rc = %Rrc\n", rc); + } + return rc; +} diff --git a/src/VBox/Additions/x11/undefined_xfree86 b/src/VBox/Additions/x11/undefined_xfree86 new file mode 100644 index 00000000..e80b6819 --- /dev/null +++ b/src/VBox/Additions/x11/undefined_xfree86 @@ -0,0 +1,1188 @@ +# This file contains the complete list, excluding modules of undefined symbols +# which are allowed in XFree86 4.3 binary driver modules. + +AddCallback +AddEnabledDevice +AddExtension +AddExtensionAlias +AddResource +AdjustWaitForDelay +AllocateClientPrivate +AllocateClientPrivateIndex +AllocateColormapPrivateIndex +AllocateGCPrivate +AllocateGCPrivateIndex +AllocatePixmap +AllocatePixmapPrivate +AllocatePixmapPrivateIndex +AllocateScreenPrivateIndex +AllocateWindowPrivate +AllocateWindowPrivateIndex +AllocColor +_alpha_inb +_alpha_inl +_alpha_inw +_alpha_outb +_alpha_outl +_alpha_outw +AltICD2061SetClock +AssignTypeAndName +Att409SetClock +AttendClient +BadShmSegCode +BitOrderInvert +BufFileRead +BufFileWrite +_bus_base +byte_reversed +ChangeGC +ChangeResourceValue +ChangeWindowAttributes +ChangeWindowProperty +CheckCursorConfinement +CheckExtension +CheckFSFormat +CheckWindowOptionalNeed +Chrontel8391SetClock +CirrusFindClock +CirrusSetClock +clients +ClientSleep +ClientSleepUntil +ClientStateCallback +ClientTimeToServerTime +ClientWakeup +CloseFont +commonCalcClock +CompareTimeStamps +ConfiguredMonitor +CopyGC +CopyISOLatin1Lowered +CopySwap32Write +CreateColormap +CreateFontRec +CreateGC +CreateNewResourceClass +CreateNewResourceType +CreateScratchGC +CreateUnclippedWinSize +CreateWindow +currentMaxClients +CurrentSelections +currentTime +dacInTi3026IndReg +dacOutTi3026IndReg +debug_inb +debug_inl +debug_inw +debug_outb +debug_outl +debug_outw +DeclareExtensionSecurity +defaultColorVisualClass +defaultDPMSEnabled +DeleteCallback +DeleteProperty +DeliverEvents +deltaSaveUndersViewable +DestroyFontRec +DeviceEventCallback +DGAActive +DGAAvailable +DGABlitRect +DGABlitTransRect +DGAChangePixmapMode +DGACloseFramebuffer +DGACreateColormap +DGAFillRect +DGAGetModeInfo +DGAGetModes +DGAGetOldDGAMode +DGAGetViewportStatus +DGAInit +DGAInstallCmap +DGAOpenFramebuffer +DGASelectInput +DGASetInputMode +DGASetMode +DGASetViewport +DGASync +dispatchException +div +__div64 +__divdf3 +__divdi3 +__divl +__divlu +__divq +__divqu +__divsf3 +__divsi3 +dixChangeGC +DoChangeGC +DPMSCapableFlag +DPMSDisabledSwitch +DPMSEnabled +DPMSEnabledSwitch +DPMSGet +DPMSOffTime +DPMSPowerLevel +DPMSSet +DPMSStandbyTime +DPMSSupported +DPMSSuspendTime +DuplicateModule +eieio +erel->rel->r_info +Error +ErrorF +Et4000AltICD2061SetClock +ET4000gendacSetClock +ET4000stg1703SetClock +ET6000SetClock +EventCallback +EventSwapVector +ex +FakeAllocColor +FakeClientID +FakeFreeColor +FatalError +FindAllClientResources +FindClientResourcesByType +FindWindowWithOptional +FlushCallback +FontCacheChangeSettings +FontCacheCloseCache +FontCacheGetBitmap +FontCacheGetEntry +FontCacheGetSettings +FontCacheGetStatistics +FontCacheInsertEntry +FontCacheOpenCache +FontCacheSearchEntry +FontComputeInfoAccelerators +FontCouldBeTerminal +FontDefaultFormat +FontEncDirectory +FontEncFind +FontEncFromXLFD +FontEncMapFind +FontEncName +font_encoding_find +font_encoding_from_xlfd +font_encoding_name +font_encoding_recode +FontEncRecode +FontFileBitmapSources +FontFileClose +FontFileCloseFont +FontFileCompleteXLFD +FontFileCountDashes +FontFileFindNameInDir +FontFileMatchRenderer +FontFileOpen +FontFileOpenBitmap +FontFilePriorityRegisterRenderer +FontFileRegisterRenderer +FontMapFind +FontMapReverse +FontMapReverseFree +FontParseXLFDName +FontToXError +FourByteSwap +fpe_functions +FreeColors +FreeCursor +FreeGC +FreeResource +FreeResourceByType +FreeScratchGC +FreeScratchPixmapHeader +func + func +gendacMNToClock +GetGlyphs +GetScratchGC +GetScratchPixmapHeader +GetTimeInMillis +GetXIDList +GetXIDRange +GiveUp +globalSerialNumber +GrabInProgress +GravityTranslate +header +IBMRGBSetClock +ICS2595SetClock +ICS5342SetClock +identifyEncodingFile +IgnoreClient +inb +_inb +InitButtonClassDeviceStruct +InitFocusClassDeviceStruct +InitKeyboardDeviceStruct +InitKeyClassDeviceStruct +InitLedFeedbackClassDeviceStruct +InitPointerDeviceStruct +InitProximityClassDeviceStruct +InitPtrFeedbackClassDeviceStruct +InitValuatorAxisStruct +InitValuatorClassDeviceStruct +inl +_inl +inputInfo +inw +_inw +ioBase +isItTimeToYield +lastDeviceEventTime +lastResourceType +ldl_brx +ldl_u +ldq_u +ldw_brx +ldw_u +LegalNewID +LoaderCheckUnresolved +LoaderDefaultFunc +LoaderErrorMsg +LoaderFreeDirList +LoaderGetOS +LoaderListDirs +LoaderRefSymbols +LoaderRefSymLists +LoaderReqSymbols +LoaderReqSymLists +LoaderSymbol +LoadExtension +LoadFont +LoadGlyphs +LoadSubModule +LocalClient +LookupClient +LookupDrawable +LookupIDByClass +LookupIDByType +LookupKeyboardDevice +LookupPointerDevice +LookupWindow +MakeAtom +MakeClientGrabImpervious +MakeClientGrabPervious +MakeWindowOptional +MapWindow +mem_barrier +memcpy +"memcpy",xf86memcpy +memset +"memset",xf86memset +miAllocateGCPrivateIndex +miChangeBorderWidth +miChangeClip +miChangeGC +miClearDrawable +miClearToBackground +miClearVisualTypes +miClipNotify +miClipSpans +miCompositeRects +miComputeCompositeClip +miComputeCompositeRegion +miCopyArea +miCopyClip +miCopyGC +miCopyPlane +miCreateDefColormap +miCreateGCOps +miCreateScreenResources +miDCInitialize +miDestroyClip +miDestroyGC +miDestroyGCOps +miEmptyBox +miEmptyData +miExpandDirectColors +miFillArcSetup +miFillArcSliceSetup +miFillConvexPoly +miFillPolygon +miFindMaxBand +miGetDefaultVisualMask +miGetImage +miGetScreenPixmap +miGlyphExtents +miGlyphs +miHandleExposures +miHandleValidateExposures +miHookInitVisuals +miImageGlyphBlt +miImageText16 +miImageText8 +miInitializeBackingStore +miInitializeBanking +miInitializeColormap +miInitOverlay +miInitVisuals +miInitVisualsProc +miInstallColormap +miInstalledMaps +miIntersect +miInverse +miListInstalledColormaps +miModifyBanking +miModifyPixmapHeader +MinorOpcodeOfRequest +miOverlayCollectUnderlayRegions +miOverlayComputeCompositeClip +miOverlayCopyUnderlay +miOverlayGetPrivateClips +miOverlaySetRootClip +miOverlaySetTransFunction +miPaintWindow +miPictureInit +miPointerAbsoluteCursor +miPointerCurrentScreen +miPointerGetMotionBufferSize +miPointerGetMotionEvents +miPointerInitialize +miPointerPosition +miPointerScreenIndex +miPointerWarpCursor +miPointInRegion +miPolyArc +miPolyBuildEdge +miPolyBuildPoly +miPolyFillArc +miPolyFillRect +miPolyGlyphBlt +miPolyPoint +miPolyRectangle +miPolySegment +miPolyText16 +miPolyText8 +miPushPixels +miPutImage +miRecolorCursor +miRectAlloc +miRectIn +miRectsToRegion +miRegionAppend +miRegionCopy +miRegionCreate +miRegionDestroy +miRegionEmpty +miRegionExtents +miRegionInit +miRegionNotEmpty +miRegionReset +miRegionUninit +miRegionValidate +miResolveColor +miRoundCapClip +miRoundJoinClip +MiscExtApply +MiscExtCreateStruct +MiscExtDestroyStruct +MiscExtGetFilePaths +MiscExtGetKbdSettings +MiscExtGetKbdValue +MiscExtGetMouseSettings +MiscExtGetMouseValue +MiscExtSetGrabKeysState +MiscExtSetKbdValue +MiscExtSetMouseDevice +MiscExtSetMouseValue +miScreenInit +miSegregateChildren +miSendGraphicsExpose +miSetPixmapDepths +miSetScreenPixmap +miSetShape +miSetVisualTypes +miSetVisualTypesAndMasks +miSetZeroLineBias +miShapedWindowIn +miSpritePointerFuncs +miStepDash +miSubtract +miTranslateRegion +miUninstallColormap +miUnion +miWideDash +miWideLine +miWindowExposures +miZeroArcSetup +miZeroClipLine +miZeroDashLine +miZeroLine +miZeroLineScreenIndex +miZeroPolyArc +__moddi3 +__modsi3 +monitorResolution +MoveWindowInStack +mul +__mul64 +__muldf3 +__muldi3 +__mulsf3 +__mulsi3 +Must_have_memory +NameForAtom + name, func + name, var +NewCurrentScreen +NoopDDA +noPanoramiXExtension +NotClippedByChildren +noTestExtensions +NotImplemented +noXkbExtension +NumCurrentSelections +numSaveUndersViewable +Ones +outb +_outb +outl +_outl +outw +_outw +panoramiXdataPtr +PanoramiXNumScreens +PciAvoid +pciBusAddrToHostAddr +pciFindFirst +pciFindNext +pciHostAddrToBusAddr +pciNumBuses +pciReadByte +pciReadLong +pciReadWord +pciSetBitsLong +pciTag +pciWriteByte +pciWriteLong +pciWriteWord +permitOldBugs +PictureAddFilter +PictureGetSubpixelOrder +PictureInit +PictureScreenPrivateIndex +PictureSetFilterAlias +PictureSetSubpixelOrder +PictureTransformPoint +PixmapWidthPaddingInfo +PointerConfinedToScreen +ProcBadRequest +ProcVector +QueryColors +QueryGlyphExtents +QueueWorkProc +RegisterBlockAndWakeupHandlers +RegisterResourceName +rel[i].r_info +rel->r_info +rem +__reml +__remlu +RemoveBlockAndWakeupHandlers +RemoveEnabledDevice +__remq +__remqu +RepadBitmap +ReplyCallback +ReplySwapVector +res8514Exclusive +res8514Shared +ResetCurrentRequest +ResizeChildrenWinSize +ResourceNames +_restf14 +_restf17 +_restf18 +_restf19 +_restf20 +_restf22 +_restf23 +_restf24 +_restf25 +_restf26 +_restf27 +_restf28 +_restf29 +resVgaExclusive +resVgaIoShared +resVgaMemShared +resVgaShared +resVgaSparseExclusive +resVgaSparseShared +resVgaUnusedExclusive +resVgaUnusedShared +S3AuroraSetClock +S3gendacSetClock +s3IBMRGB_Init +s3IBMRGB_Probe +s3InIBMRGBIndReg +s3OutIBMRGBIndReg +S3Trio64V2SetClock +S3TrioSetClock +savedScreenInfo +_savef14 +_savef17 +_savef18 +_savef19 +_savef20 +_savef22 +_savef23 +_savef24 +_savef25 +_savef26 +_savef27 +_savef28 +_savef29 +SaveScreens +SC11412SetClock +screenInfo +screenIsSaved +ScreenSaverBlanking +ScreenSaverTime +SecurityLookupDrawable +SecurityLookupIDByClass +SecurityLookupIDByType +SecurityLookupWindow +SendErrorToClient +SendMappingNotify +SendVisibilityNotify +serverClient +serverGeneration +ServerGrabCallback +SetBorderSize +SetClipRects +SetCriticalEvent +SetCriticalOutputPending +SetDashes +SetInputCheck +SetTimeSinceLastInputEvent +SetWinSize +ShmCompletionCode +ShmSegType +SkippedRequestsCallback +sparcPromClose +sparcPromGetBool +sparcPromGetProperty +sparcPromInit +StandardMinorOpcode +STG1703getIndex +STG1703magic +STG1703SetClock +STG1703setIndex +stl_brx +stl_u +StoreColors +stq_u +stw_brx +stw_u +Swap32Write +SwapColorItem +SwapConnSetupInfo +SwapConnSetupPrefix +SwapLongs +SwapShorts +sysctlbyname +TellGainedMap +TellLostMap +Ti3025SetClock +Ti3026SetClock +Ti3030SetClock +TimerCancel +TimerFree +TimerSet +TraverseTree +TryClientEvents +TwoByteSwap +TypeMask +udiv +__udivdi3 +__udivsi3 +__umoddi3 +__umodsi3 +umul +UnloadSubModule +UnmapWindow +UpdateCurrentTime +UpdateCurrentTimeIf +urem +ValidateGC +ValidAtom + var +VerifyRectOrder +VErrorF +VidModeAddModeline +VidModeCheckModeForDriver +VidModeCheckModeForMonitor +VidModeCopyMode +VidModeCreateMode +VidModeDeleteModeline +VidModeExtensionInit +VidModeGetClocks +VidModeGetCurrentModeline +VidModeGetDotClock +VidModeGetFirstModeline +VidModeGetGamma +VidModeGetGammaRamp +VidModeGetGammaRampSize +VidModeGetModeValue +VidModeGetMonitor +VidModeGetMonitorValue +VidModeGetNextModeline +VidModeGetNumOfClocks +VidModeGetNumOfModes +VidModeGetViewPort +VidModeLockZoom +VidModeSetCrtcForMode +VidModeSetGamma +VidModeSetGammaRamp +VidModeSetModeValue +VidModeSetViewPort +VidModeSwitchMode +VidModeZoomViewport +WalkTree +WindowsRestructured +WindowTable +WriteEventsToClient +write_mem_barrier +WriteToClient +x +Xalloc +Xcalloc +XDGAEventBase +xf86abort +xf86abs +xf86access +xf86acos +xf86AcquireGART +xf86AddDeviceToConfigure +xf86AddDriver +xf86AddEnabledDevice +xf86AddEntityToScreen +xf86AddInputDriver +xf86AddInputHandler +xf86AddModuleInfo +xf86AddNewOption +xf86AddPixFormat +xf86AddResToList +xf86AgpGARTSupported +xf86AllocateEntityPrivateIndex +xf86AllocateGARTMemory +xf86AllocateInput +xf86AllocateLinearOffscreenArea +xf86AllocateOffscreenArea +xf86AllocateOffscreenLinear +xf86AllocateScreen +xf86AllocateScrnInfoPrivateIndex +xf86asin +xf86atan +xf86atan2 +xf86atof +xf86atoi +xf86atol +xf86BindGARTMemory +xf86BlockSIGIO +xf86Break1 +xf86Break2 +xf86Break3 +xf86bsearch +xf86BusToMem +xf86bzero +xf86calloc +xf86CaughtSignal +xf86ceil +xf86CheckIfOptionUsed +xf86CheckIfOptionUsedByName +xf86CheckModeForDriver +xf86CheckModeForMonitor +xf86CheckMTRR +xf86CheckPciGAType +xf86CheckPciMemBase +xf86CheckPciSlot +xf86ChkConflict +xf86chmod +xf86chown +xf86ClaimFbSlot +xf86ClaimFixedResources +xf86ClaimIsaSlot +xf86ClaimNoSlot +xf86ClaimPciSlot +xf86clearerr +xf86ClearPrimInitDone +xf86close +xf86closedir +xf86CloseSerial +xf86clrdaccommbit +xf86CollectInputOptions +xf86CollectOptions +xf86CommonSpecialKey +xf86ComparePciBusString +xf86ConfigActiveIsaEntity +xf86ConfigActivePciEntity +xf86ConfigDRI +xf86ConfigFbEntity +xf86ConfigIsaEntity +xf86ConfigIsaEntityInactive +xf86ConfigPciEntity +xf86ConfigPciEntityInactive +xf86cos +xf86CurrentScreen +xf86dactocomm +xf86dactopel +xf86DeallocateResourcesForEntity +xf86DeleteDriver +xf86DeleteInput +xf86DeleteMode +xf86DeleteModuleInfo +xf86DeleteScreen +xf86DeregisterStateChangeNotificationCallback +xf86DisableInputHandler +xf86DisableInterrupts +xf86DisableIO +xf86DisableRandR +xf86DPMSInit +xf86DrvMsg +xf86DrvMsgVerb +xf86DummyVar1 +xf86DummyVar2 +xf86DummyVar3 +xf86DupResList +xf86EnableAccess +xf86EnableAGP +xf86EnableDisableFBAccess +xf86EnableInputHandler +xf86EnableInterrupts +xf86EnableIO +xf86EnablePciBusMaster +xf86EnableVTSwitch +xf86EnterServerState +xf86eqEnqueue +xf86errno +xf86ErrorF +xf86ErrorFVerb +xf86execl +xf86exit +xf86exp +xf86fabs +xf86FBManagerRunning +xf86fclose +xf86feof +xf86ferror +xf86fflush +xf86ffs +xf86fgetc +xf86fgetpos +xf86fgets +xf86FindOption +xf86FindOptionValue +xf86FindPciClass +xf86FindPciDeviceVendor +xf86FindScreenForEntity +xf86FindXvOptions +xf86finite +xf86FirstLocalDevice +xf86FixPciResource +xf86floor +xf86FlushInput +xf86fmod +xf86fopen +xf86FormatPciBusNumber +xf86fpossize +xf86fprintf +xf86fputc +xf86fputs +xf86fread +xf86free +xf86FreeOffscreenArea +xf86FreeOffscreenLinear +xf86FreeResList +xf86freopen +xf86frexp +xf86fscanf +xf86fseek +xf86fsetpos +xf86fstat +xf86ftell +xf86fwrite +xf86GARTCloseScreen +xf86GetAGPInfo +xf86GetAllowMouseOpenFail +xf86GetBlock +xf86GetBppFromDepth +xf86getc +xf86GetClocks +xf86getdaccomm +xf86GetDepth +xf86GetDevFromEntity +xf86getegid +xf86GetEntityForSbusInfo +xf86GetEntityInfo +xf86GetEntityPrivate +xf86getenv +xf86GetErrno +xf86geteuid +xf86GetFlipPixels +xf86GetGamma +xf86getjmptype +xf86GetLastScrnFlag +xf86GetModInDevAllowNonLocal +xf86GetModInDevEnabled +xf86GetModuleVersion +xf86GetMotionEvents +xf86GetNearestClock +xf86GetNumEntityInstances +xf86GetOptValBool +xf86GetOptValFreq +xf86GetOptValInteger +xf86GetOptValReal +xf86GetOptValString +xf86GetOptValULong +xf86GetOS +xf86getpagesize +xf86GetPciConfigInfo +xf86GetPciDomain +xf86GetPciEntity +xf86GetPciInfoForEntity +xf86GetPciVideoInfo +xf86getpid +xf86GetPix24 +xf86GetPixFormat +xf86GetPointerScreenFuncs +xf86GetSbusInfoForEntity +xf86getsecs +xf86GetSerialModemState +xf86GetServerName +xf86GetSparse +xf86GetVerbosity +xf86GetVersion +xf86GetVidModeAllowNonLocal +xf86GetVidModeEnabled +xf86GetVisualName +xf86GetWeight +xf86HandleColormaps +xf86HUGE_VAL +xf86hypot +xf86InitFBManager +xf86InitFBManagerArea +xf86InitFBManagerRegion +xf86InitialCheckModeForDriver +xf86InitValuatorAxisStruct +xf86InitValuatorDefaults +xf86InstallSIGIOHandler +xf86inSuspend +xf86InterceptSignals +xf86ioctl +xf86IODelay +xf86isalnum +xf86isalpha +xf86iscntrl +xf86IsCorePointer +xf86isdigit +xf86IsEntityPrimary +xf86IsEntitySharable +xf86IsEntityShared +xf86isgraph +xf86islower +xf86IsOptionSet +xf86IsPc98 +xf86IsPciDevPresent +xf86IsPrimaryIsa +xf86IsPrimaryPci +xf86IsPrimInitDone +xf86isprint +xf86ispunct +xf86IsScreenPrimary +xf86isspace +xf86IsUnblank +xf86isupper +xf86isxdigit +xf86JoinResLists +xf86labs +xf86ldexp +xf86LinearVidMem +xf86LoadDrvSubModule +xf86LoaderCheckSymbol +xf86LoaderRefSymbols +xf86LoaderRefSymLists +xf86LoaderReqSymbols +xf86LoaderReqSymLists +xf86LoadKernelModule +xf86LoadOneModule +xf86LoadSubModule +xf86log +xf86log10 +"xf86longjmp",longjmp +xf86LookupMode +xf86lseek +xf86malloc +xf86MapDomainIO +xf86MapDomainMemory +xf86MapPciMem +xf86MapReadSideEffects +xf86MapSbusMem +xf86MapVidMem +xf86MarkOptionUsed +xf86MarkOptionUsedByName +xf86MatchDevice +xf86MatchIsaInstances +xf86MatchPciInstances +xf86MatchSbusInstances +xf86memchr +xf86memcmp +xf86memcpy +xf86memmove +xf86memset +xf86MemToBus +xf86mkdir +xf86mknod +xf86mmap +xf86ModeStatusToString +xf86modf +xf86MotionHistoryAllocate +xf86Msg +xf86MsgVerb +xf86munmap +xf86NameCmp +xf86NewOption +xf86NewSerialNumber +xf86NextOption +xf86NoSharedResources +xf86open +xf86opendir +xf86OpenSerial +xf86OptionListCreate +xf86OptionListFree +xf86OptionListMerge +xf86OptionListReport +xf86OptionName +xf86OptionValue +xf86OSKbdPreInit +xf86OSMouseInit +xf86p8bit +xf86ParseIsaBusString +xf86ParsePciBusString +xf86perror +xf86PixmapIndex +xf86PostButtonEvent +xf86PostKeyboardEvent +xf86PostKeyEvent +xf86PostMotionEvent +xf86PostProximityEvent +xf86pow +xf86PrintChipsets +xf86PrintDepthBpp +xf86printf +xf86PrintModes +xf86PrintResList +xf86ProcessCommonOptions +xf86ProcessOptions +xf86PruneDriverModes +xf86PurgeUnlockedOffscreenAreas +xf86qsort +xf86QueryLargestOffscreenArea +xf86QueryLargestOffscreenLinear +xf86QueueAsyncEvent +xf86read +xf86ReadBIOS +xf86readdir +xf86ReadDomainMemory +xf86ReadMmio16 +xf86ReadMmio32 +xf86ReadMmio8 +xf86ReadPciBIOS +xf86ReadSerial +xf86realloc +xf86ReallocatePciResources +xf86RegisterFreeBoxCallback +xf86RegisterOffscreenManager +xf86RegisterResources +xf86RegisterRootWindowProperty +xf86RegisterStateChangeNotificationCallback +xf86ReleaseGART +xf86remove +xf86RemoveEnabledDevice +xf86RemoveEntityFromScreen +xf86RemoveInputHandler +xf86RemoveSIGIOHandler +xf86rename +xf86ReplaceBoolOption +xf86ReplaceIntOption +xf86ReplaceStrOption +xf86ResizeOffscreenArea +xf86ResizeOffscreenLinear +xf86ReturnOptValBool +xf86rewind +xf86rewinddir +xf86SbusHandleColormaps +xf86SbusHideOsHwCursor +xf86SbusSetOsHwCursorCmap +xf86SbusUseBuiltinMode +xf86ScaleAxis +xf86scanpci +xf86ScreenIndex +xf86Screens +xf86SerialModemClearBits +xf86SerialModemSetBits +xf86SerialSendBreak +xf86ServerIsExiting +xf86ServerIsOnlyDetecting +xf86ServerIsOnlyProbing +xf86ServerIsResetting +xf86SetAccessFuncs +xf86SetBackingStore +xf86SetBlackWhitePixels +xf86SetBoolOption +xf86setbuf +xf86SetCrtcForModes +xf86SetCurrentAccess +xf86setdaccomm +xf86setdaccommbit +xf86SetDefaultVisual +xf86SetDepthBpp +xf86SetDpi +xf86SetEntityFuncs +xf86SetEntityInstanceForScreen +xf86SetEntitySharable +xf86SetEntityShared +xf86SetGamma +xf86SetIntOption +xf86setjmp +xf86setjmp1 +xf86setjmp1_arg2 +"xf86setjmp1",__sigsetjmp +xf86setjmperror +"xf86setjmp",setjmp +xf86SetLastScrnFlag +xf86SetOperatingState +xf86SetPciVideo +xf86SetPrimInitDone +xf86SetPriority +xf86SetRealOption +xf86SetSerial +xf86SetSerialModemState +xf86SetSerialSpeed +xf86SetSilkenMouse +xf86SetStrOption +xf86setvbuf +xf86SetWeight +xf86shmat +xf86shmctl +xf86shmdt +xf86shmget +xf86ShowClockRanges +xf86ShowClocks +xf86ShowUnusedOptions +xf86sin +xf86sleep +xf86SlowBcopy +xf86SlowBCopyFromBus +xf86SlowBCopyToBus +xf86snprintf +xf86SoundKbdBell +xf86sprintf +xf86SPTimestamp +xf86sqrt +xf86sscanf +xf86stat +xf86stderr +xf86stdin +xf86stdout +xf86STimestamp +xf86strcasecmp +xf86strcat +xf86strchr +xf86strcmp +xf86strcpy +xf86strcspn +xf86strdup +xf86strerror +xf86StringToToken +xf86strlen +xf86strncasecmp +xf86strncat +xf86strncmp +xf86strncpy +xf86strpbrk +xf86strrchr +xf86strspn +xf86strstr +xf86strtod +xf86strtok +xf86strtol +xf86strtoul +xf86tan +xf86tmpfile +xf86TokenToOptinfo +xf86TokenToOptName +xf86TokenToString +xf86tolower +xf86toupper +xf86UDelay +xf86UnbindGARTMemory +xf86UnblockSIGIO +xf86ungetc +xf86UnloadSubModule +xf86UnmapSbusMem +xf86UnMapVidMem +xf86usleep +xf86ValidateModes +xf86VDrvMsgVerb +xf86vfprintf +xf86vsnprintf +xf86vsprintf +xf86WaitForInput +xf86write +xf86WriteMmio16 +xf86WriteMmio32 +xf86WriteMmio8 +xf86WriteMmioNB16 +xf86WriteMmioNB32 +xf86WriteMmioNB8 +xf86writepci +xf86WriteSerial +xf86XInputSetScreen +xf86XInputSetSendCoreEvents +xf86XVAllocateVideoAdaptorRec +xf86XVClipVideoHelper +xf86XVFillKeyHelper +xf86XVFreeVideoAdaptorRec +xf86XVListGenericAdaptors +xf86XvMCScreenInit +xf86XVQueryOffscreenImages +xf86XVRegisterGenericAdaptorDriver +xf86XVRegisterOffscreenImages +xf86XVScreenInit +Xfree +XineramaDeleteResource +XineramaGetCursorScreen +XineramaRegisterConnectionBlockCallback +XisbBlockDuration +XisbFree +XisbNew +XisbRead +XisbTrace +XisbWrite +XkbInitKeyboardDeviceStruct +XkbSetRulesDflts +XNFalloc +XNFcalloc +XNFrealloc +XNFstrdup +XRC_DRAWABLE +Xrealloc +XRT_COLORMAP +XRT_GC +XRT_PIXMAP +XRT_WINDOW +Xstrdup +XvGetRTPortProc +XvGetScreenIndexProc +XvMCScreenInitProc +XvScreenInitProc diff --git a/src/VBox/Additions/x11/undefined_xfree86_modules b/src/VBox/Additions/x11/undefined_xfree86_modules new file mode 100644 index 00000000..9a800b2c --- /dev/null +++ b/src/VBox/Additions/x11/undefined_xfree86_modules @@ -0,0 +1,16 @@ +# This file contains the list of symbols from XFree86 modules which we use. +# These symbols must also be explicitly declared in the driver code. + +fbPictureInit +fbScreenInit +ShadowFBInit2 +vgaHWFreeHWRec +vgaHWGetHWRec +vgaHWGetIndex +vgaHWGetIOBase +vgaHWRestore +vgaHWSave +vgaHWSetStdFuncs +xf86CreateCursorInfoRec +xf86DestroyCursorInfoRec +xf86InitCursor diff --git a/src/VBox/Additions/x11/undefined_xorg b/src/VBox/Additions/x11/undefined_xorg new file mode 100644 index 00000000..1d34ee9e --- /dev/null +++ b/src/VBox/Additions/x11/undefined_xorg @@ -0,0 +1,183 @@ +# This file contains a non-exhaustive list of symbols which are allowed to +# be undefined in X.Org Server binary driver modules which were not allowed +# in XFree86 4.3. Some of these may only be allowed on some platforms, but +# it fixing that would add additional complexity. + +__assert_fail +calloc +chdir +chmod +chown +close +closedir +__ctype_b_loc +__ctype_mask +__cxa_finalize +__deregister_frame_info_bases +DRI2CloseScreen +DRI2ScreenInit +DRICloseScreen +DRICreateInfoRec +DRICreatePCIBusID +DRIDestroyInfoRec +DRIFinishScreenInit +DRILock +DRIQueryVersion +DRIScreenInit +DRIUnlock +drmClose +drmDropMaster +drmFreeVersion +drmGetVersion +drmIoctl +drmModeGetResources +drmModeFreeResources +drmSetMaster +___errno +__errno_location +fbPictureInit +fbScreenInit +fchmod +fchown +fcntl +fflush +flock +fprintf +__fprintf_chk +fputs +free +fstat +fsync +ftruncate +ftruncate64 +futimes +fwrite +__fxstat64 +getcwd +getenv +geteuid +GetMotionHistory +GetMotionHistorySize +getpwuid_r +GlxSetVisualConfigs +__gmon_start__ +iconv +iconv_close +iconv_open +__iob +ioctl +__isoc99_sscanf +isspace +_ITM_deregisterTMCloneTable +_ITM_registerTMCloneTable +_Jv_RegisterClasses +lchown +lseek +lseek64 +lstat +__lxstat64 +malloc +memalign +memchr +memcmp +__memcpy_chk +memmove +miPointerGetScreen +mmap64 +mprotect +munmap +nanosleep +nl_langinfo +open +open64 +opendir +pci_device_map_range +pci_device_unmap_range +posix_memalign +pthread_self +pthread_sigmask +pthread_yield +putenv +read +readdir +readdir64 +realloc +realpath +__realpath_chk +__register_frame_info_bases +rename +RRCrtcNotify +RRGetInfo +RRScreenSizeSet +RRTellChanged +setenv +ShadowFBInit2 +sigdelset +sigfillset +snprintf +__snprintf_chk +sprintf +__sprintf_chk +sscanf +__stack_chk_fail +stat +stderr +strchr +strcmp +strcpy +strlen +strncat +__strncat_chk +strncmp +strncpy +__strncpy_chk +strpbrk +strstr +strtoul +__strtoul_internal +SwappedProcVector +symlink +tolower +unlink +unsetenv +utimes +vfprintf +__vfprintf_chk +vgaHWFreeHWRec +vgaHWGetHWRec +vgaHWGetIndex +vgaHWGetIOBase +vgaHWRestore +vgaHWSave +vgaHWSetStdFuncs +vsnprintf +__vsnprintf_chk +write +xf86AddGeneralHandler +xf86CreateCursorInfoRec +xf86CrtcConfigInit +xf86CrtcConfigPrivateIndex +xf86CrtcCreate +xf86CrtcScreenInit +xf86CrtcSetMode +xf86CrtcSetSizeRange +xf86DestroyCursorInfoRec +xf86DPMSSet +xf86InitCursor +xf86InitialConfiguration +xf86InterpretEDID +xf86ModesAdd +xf86OutputCreate +xf86OutputSetEDID +xf86OutputUseScreenMonitor +xf86RandR12GetOriginalVirtualSize +xf86RemoveGeneralHandler +xf86SaveScreen +xf86ScreenToScrn +xf86ScrnToScreen +xf86SetDesiredModes +xf86SetModeDefaultName +xf86SetSingleMode +xf86UpdateDesktopDimensions +XNFcallocarray +__xstat64 diff --git a/src/VBox/Additions/x11/vboxmouse/Makefile.kmk b/src/VBox/Additions/x11/vboxmouse/Makefile.kmk new file mode 100644 index 00000000..8bc81b30 --- /dev/null +++ b/src/VBox/Additions/x11/vboxmouse/Makefile.kmk @@ -0,0 +1,286 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the VBox Additions XFree86 and X.org mouse drivers. +# + +# +# Copyright (C) 2006-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# + +SUB_DEPTH = ../../../../.. +include $(KBUILD_PATH)/subheader.kmk + +vboxmouse_xorg_INCS = \ + $(VBOX_PATH_X11_ROOT)/inputproto-1.9.99.902 \ + $(VBOX_PATH_X11_ROOT)/libpciaccess-0.10.8 \ + $(VBOX_PATH_X11_ROOT)/pixman-0.16.0 \ + $(VBOX_PATH_X11_ROOT)/xextproto-7.1.1 \ + $(VBOX_PATH_X11_ROOT)/xproto-7.0.18 + +# +# vboxmouse_drv +# +if1of ($(KBUILD_TARGET), linux) + SYSMODS += vboxmouse_drv + vboxmouse_drv_TEMPLATE = VBOXGUESTR3XF86MOD + vboxmouse_drv_DEFS.linux = linux + vboxmouse_drv_DEFS.x86 += __i386__ + # This one has to be defined when building server code on systems where + # unsigned long is 64bits + vboxmouse_drv_DEFS.amd64 += _XSERVER64 + vboxmouse_drv_DEFS += \ + _POSIX_C_SOURCE=199309L _POSIX_SOURCE _XOPEN_SOURCE _DEFAULT_SOURCE \ + _BSD_SOURCE _SVID_SOURCE _GNU_SOURCE SHAPE XINPUT XKB LBX XAPPGROUP \ + XCSECURITY TOGCUP XF86BIGFONT DPMSExtension PIXPRIV PANORAMIX RENDER \ + GCCUSESGAS AVOID_GLYPHBLT PIXPRIV SINGLEDEPTH XFreeXDGA XvExtension \ + XFree86LOADER XFree86Server XF86VIDMODE XvMCExtension SMART_SCHEDULE \ + BUILDDEBUG X_BYTE_ORDER=X_LITTLE_ENDIAN DNDEBUG FUNCPROTO=15 NARROWPROTO \ + IN_MODULE XFree86Module PNP_MOUSE IN_XF86_MODULE + vboxmouse_drv_INCS := \ + $(VBOX_PATH_X11_ROOT)/XFree86-4.3 \ + $(VBOX_PATH_X11_ROOT)/XFree86-4.3/X11 \ + $(VBOX_PATH_X11_ROOT)/XFree86-4.3/X11/extensions \ + $(VBOX_PATH_X11_ROOT)/XFree86-4.3/Xserver \ + $(PATH_SUB_CURRENT) + vboxmouse_drv_SOURCES = \ + vboxmouse.c + # Any global symbols in the driver object files will be added to XFree86's + # symbol table, which can cause problems if we e.g. define a symbol in two + # modules. + vboxmouse_drv_POST_CMDS = \ + objcopy --keep-global-symbol vboxmouseModuleData $(out) $(out)-objcopy$$(NLTAB) \ + $(MV) -f $(out)-objcopy $(out) +endif + + +# +# vboxmouse_drv_70 +# +DLLS += vboxmouse_drv_70 +vboxmouse_drv_70_TEMPLATE = VBOXGUESTR3XORGMOD +vboxmouse_drv_70_DEFS = \ + XFree86Server IN_MODULE XFree86Module XFree86LOADER XINPUT XORG_7X IN_XF86_MODULE DONT_DEFINE_WRAPPERS NO_ANSIC +vboxmouse_drv_70_INCS := \ + $(vboxmouse_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.0.1 \ + $(PATH_SUB_CURRENT) +vboxmouse_drv_70_SOURCES = \ + vboxmouse.c + + +# +# vboxmouse_drv_71 +# +DLLS += vboxmouse_drv_71 +vboxmouse_drv_71_TEMPLATE = VBOXGUESTR3XORGMOD +vboxmouse_drv_71_DEFS := $(vboxmouse_drv_70_DEFS) NO_ANSIC +vboxmouse_drv_71_INCS := \ + $(vboxmouse_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.1.0 \ + $(PATH_SUB_CURRENT) +vboxmouse_drv_71_SOURCES = \ + vboxmouse.c + + +# +# vboxmouse_drv_13 +# +DLLS += vboxmouse_drv_13 +vboxmouse_drv_13_TEMPLATE = VBOXGUESTR3XORGMOD +vboxmouse_drv_13_DEFS := $(vboxmouse_drv_70_DEFS) NO_ANSIC +vboxmouse_drv_13_INCS := \ + $(vboxmouse_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.3.0.0 \ + $(PATH_SUB_CURRENT) +vboxmouse_drv_13_SOURCES = \ + vboxmouse.c + + +# +# vboxmouse_drv_14 +# +DLLS += vboxmouse_drv_14 +vboxmouse_drv_14_TEMPLATE = VBOXGUESTR3XORGMOD +vboxmouse_drv_14_DEFS := $(vboxmouse_drv_70_DEFS) NO_ANSIC +vboxmouse_drv_14_INCS := \ + $(vboxmouse_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.4.2 \ + $(PATH_SUB_CURRENT) +vboxmouse_drv_14_SOURCES = \ + vboxmouse.c + + +# +# vboxmouse_drv_15 +# +DLLS += vboxmouse_drv_15 +vboxmouse_drv_15_TEMPLATE = VBOXGUESTR3XORGMOD +vboxmouse_drv_15_DEFS := $(vboxmouse_drv_70_DEFS) NO_ANSIC +vboxmouse_drv_15_INCS := \ + $(vboxmouse_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.5.3 \ + $(PATH_SUB_CURRENT) +vboxmouse_drv_15_SOURCES = \ + vboxmouse.c + + +# +# vboxmouse_drv_16 +# +DLLS += vboxmouse_drv_16 +vboxmouse_drv_16_TEMPLATE = VBOXGUESTR3XORGMOD +vboxmouse_drv_16_DEFS := $(vboxmouse_drv_70_DEFS) NO_ANSIC +vboxmouse_drv_16_INCS := \ + $(vboxmouse_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.6.5 \ + $(PATH_SUB_CURRENT) +vboxmouse_drv_16_SOURCES = \ + vboxmouse.c + + +ifneq ($(KBUILD_TARGET), linux) + +# +# vboxmouse_drv_17 +# +DLLS += vboxmouse_drv_17 +vboxmouse_drv_17_TEMPLATE = VBOXGUESTR3XORGMOD +vboxmouse_drv_17_DEFS := $(vboxmouse_drv_70_DEFS) NO_ANSIC +vboxmouse_drv_17_INCS := \ + $(vboxmouse_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.7.7 \ + $(PATH_SUB_CURRENT) +vboxmouse_drv_17_SOURCES = \ + vboxmouse.c + + +# +# vboxmouse_drv_18 +# +DLLS += vboxmouse_drv_18 +vboxmouse_drv_18_TEMPLATE = VBOXGUESTR3XORGMOD +vboxmouse_drv_18_DEFS := $(vboxmouse_drv_70_DEFS) NO_ANSIC +vboxmouse_drv_18_INCS := \ + $(vboxmouse_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.8.0 \ + $(PATH_SUB_CURRENT) +vboxmouse_drv_18_SOURCES = \ + vboxmouse.c + + +# +# vboxmouse_drv_19 +# +DLLS += vboxmouse_drv_19 +vboxmouse_drv_19_TEMPLATE = VBOXGUESTR3XORGMOD +vboxmouse_drv_19_DEFS := $(vboxmouse_drv_70_DEFS) NO_ANSIC +vboxmouse_drv_19_INCS := \ + $(vboxmouse_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.9.0 \ + $(PATH_SUB_CURRENT) +vboxmouse_drv_19_SOURCES = \ + vboxmouse.c + + +# +# vboxmouse_drv_110 +# +DLLS += vboxmouse_drv_110 +vboxmouse_drv_110_TEMPLATE = VBOXGUESTR3XORGMOD +vboxmouse_drv_110_DEFS := $(vboxmouse_drv_70_DEFS) NO_ANSIC +vboxmouse_drv_110_INCS := \ + $(vboxmouse_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.10.0 \ + $(PATH_SUB_CURRENT) +vboxmouse_drv_110_SOURCES = \ + vboxmouse.c + +DLLS += vboxmouse_drv_111 +vboxmouse_drv_111_TEMPLATE = VBOXGUESTR3XORGMOD +vboxmouse_drv_111_DEFS := $(vboxmouse_drv_70_DEFS) NO_ANSIC +vboxmouse_drv_111_INCS := \ + $(vboxmouse_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.11.0 \ + $(PATH_SUB_CURRENT) +vboxmouse_drv_111_SOURCES = \ + vboxmouse.c + +DLLS += vboxmouse_drv_112 +vboxmouse_drv_112_TEMPLATE = VBOXGUESTR3XORGMOD +vboxmouse_drv_112_DEFS := $(vboxmouse_drv_70_DEFS) NO_ANSIC +vboxmouse_drv_112_INCS := \ + $(vboxmouse_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.12.0 \ + $(PATH_SUB_CURRENT) +vboxmouse_drv_112_SOURCES = \ + vboxmouse.c + +endif # neq ($(KBUILD_TARGET),linux) + + +ifdef VBOX_USE_SYSTEM_XORG_HEADERS + # As vboxmouse_drv is not needed at all for X.Org Server 1.7 and later do not + # build it in this case. + DLLS := $(filter-out vboxmouse_drv_%,$(DLLS)) + SYSMODS := $(filter-out vboxmouse_drv%,$(SYSMODS)) +endif + + +# Check the undefined symbols in the X.Org modules against lists of allowed +# symbols. Not very elegant, but it will catch problems early. + +ifdef VBOX_WITH_TESTCASES +# ifndef VBOX_ONLY_ADDITIONS + ifndef VBOX_USE_SYSTEM_XORG_HEADERS + ifeq ($(KBUILD_TARGET),linux) + ifeq ($(KBUILD_HOST_ARCH),$(KBUILD_TARGET_ARCH)) + ifndef VBOX_ONLY_SDK + VBOXMOUSE_SRC_PATH := $(PATH_SUB_CURRENT) + + ifeq ($(KBUILD_TARGET),linux) + TESTING += $(vboxmouse_drv_0_OUTDIR)/tstvboxmouse68.run + OTHERS += $(vboxmouse_drv_0_OUTDIR)/tstvboxmouse68.run +$$(vboxmouse_drv_0_OUTDIR)/tstvboxmouse68.run: $$(vboxmouse_drv_1_STAGE_TARGET) + $(QUIET)$(call MSG_L1,Checking for unresolved symbols in $<) + $(QUIET)/bin/sh $(PATH_ROOT)/src/bldprogs/checkUndefined.sh $(KBUILD_HOST) \ + "$(vboxmouse_drv_1_STAGE_TARGET)" --static "$(VBOXMOUSE_SRC_PATH)/../undefined_xfree86" "$(VBOXMOUSE_SRC_PATH)/../undefined_xfree86_modules" + $(QUIET)$(APPEND) -t "$@" "done" + endif + +## +# Using the extra expansion to replace $(ver) before eval, thus everything +# else needs escaped dollars. + define def_vboxmouse_test + TESTING += $$(vboxmouse_drv$(ver)_0_OUTDIR)/tstvboxmouse$(ver).run + OTHERS += $$(vboxmouse_drv$(ver)_0_OUTDIR)/tstvboxmouse$(ver).run + $$$$(vboxmouse_drv$(ver)_0_OUTDIR)/tstvboxmouse$(ver).run: $$$$(vboxmouse_drv$(ver)_1_STAGE_TARGET) + $$(QUIET)$$(call MSG_L1,Checking for unresolved symbols in $$<) + $$(QUIET)$$(ASH) $$(PATH_ROOT)/src/bldprogs/checkUndefined.sh $$(KBUILD_HOST) \ + $$(vboxmouse_drv$(ver)_1_STAGE_TARGET) $$(VBOXMOUSE_SRC_PATH)/../undefined_xfree86 $(VBOXMOUSE_SRC_PATH)/../undefined_xfree86_modules $$(VBOXMOUSE_SRC_PATH)/../undefined_xorg + $$(QUIET)$$(APPEND) -t "$$@" "done" + endef + + $(foreach ver, _70 _71 _13 _14 _15 _16, $(eval $(def_vboxmouse_test))) + + ifneq ($(KBUILD_TARGET), linux) + $(foreach ver, _17 _18 _19 _110 _111 _112 _113, $(eval $(def_vboxmouse_test))) + + endif # neq ($(KBUILD_TARGET),linux) + + endif # ! VBOX_ONLY_SDK + endif # eq ($(KBUILD_HOST_ARCH),$(KBUILD_TARGET_ARCH)) + endif # eq ($(KBUILD_TARGET),linux) + endif # ! VBOX_USE_SYSTEM_XORG_HEADERS +# endif # ! VBOX_ONLY_ADDITIONS +endif # VBOX_WITH_TESTCASES + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/Additions/x11/vboxmouse/vboxmouse.c b/src/VBox/Additions/x11/vboxmouse/vboxmouse.c new file mode 100644 index 00000000..774a9059 --- /dev/null +++ b/src/VBox/Additions/x11/vboxmouse/vboxmouse.c @@ -0,0 +1,364 @@ +/* $Id: vboxmouse.c $ */ +/** @file + * VirtualBox X11 Guest Additions, mouse driver for X.Org server 1.5 + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * -------------------------------------------------------------------- + * + * This code is based on evdev.c from X.Org with the following copyright + * and permission notice: + * + * Copyright © 2004-2008 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of Red Hat + * not be used in advertising or publicity pertaining to distribution + * of the software without specific, written prior permission. Red + * Hat makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied + * warranty. + * + * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL THE AUTHORS 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: + * Kristian Høgsberg (krh@redhat.com) + * Adam Jackson (ajax@redhat.com) + */ + +#include <VBox/VMMDev.h> /* for VMMDEV_MOUSE_XXX */ +#include <VBox/VBoxGuestLib.h> +#include <iprt/errcore.h> +#include <xf86.h> +#include <xf86Xinput.h> +#include <mipointer.h> + +#include <xf86Module.h> + +#ifdef VBOX_GUESTR3XF86MOD +# define _X_EXPORT +#else +# include <errno.h> +# include <fcntl.h> +# include <unistd.h> +#endif + +#include "product-generated.h" + +static void +VBoxReadInput(InputInfoPtr pInfo) +{ + uint32_t cx, cy, fFeatures; + + /* Read a byte from the device to acknowledge the event */ + char c; + int res = read(pInfo->fd, &c, 1); + NOREF(res); + /* The first test here is a workaround for an apparent bug in Xorg Server 1.5 */ + if ( +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 2 + miPointerCurrentScreen() != NULL +#else + miPointerGetScreen(pInfo->dev) != NULL +#endif + && RT_SUCCESS(VbglR3GetMouseStatus(&fFeatures, &cx, &cy)) + && (fFeatures & VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE)) + { +#if ABI_XINPUT_VERSION == SET_ABI_VERSION(2, 0) + /* Bug in the 1.4 X server series - conversion_proc was no longer + * called, but the server didn't yet do the conversion itself. */ + cx = (cx * screenInfo.screens[0]->width) / 65535; + cy = (cy * screenInfo.screens[0]->height) / 65535; +#endif + /* send absolute movement */ + xf86PostMotionEvent(pInfo->dev, 1, 0, 2, cx, cy); + } +} + +static void +VBoxPtrCtrlProc(DeviceIntPtr device, PtrCtrl *ctrl) +{ + /* Nothing to do, dix handles all settings */ + RT_NOREF(device, ctrl); +} + +static int +VBoxInit(DeviceIntPtr device) +{ + CARD8 map[2] = { 0, 1 }; +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + Atom axis_labels[2] = { 0, 0 }; + Atom button_labels[2] = { 0, 0 }; +#endif + if (!InitPointerDeviceStruct((DevicePtr)device, map, 2, +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + button_labels, +#endif +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 2 + miPointerGetMotionEvents, VBoxPtrCtrlProc, + miPointerGetMotionBufferSize() +#elif GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 3 + GetMotionHistory, VBoxPtrCtrlProc, + GetMotionHistorySize(), 2 /* Number of axes */ + +#elif GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3 + VBoxPtrCtrlProc, GetMotionHistorySize(), + 2 /* Number of axes */ +#else +# error Unsupported version of X.Org +#endif +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + , axis_labels +#endif + )) + return !Success; + + /* Tell the server about the range of axis values we report */ +#if ABI_XINPUT_VERSION <= SET_ABI_VERSION(2, 0) + xf86InitValuatorAxisStruct(device, 0, 0, -1, 1, 0, 1); + xf86InitValuatorAxisStruct(device, 1, 0, -1, 1, 0, 1); +#else + xf86InitValuatorAxisStruct(device, 0, +# if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + axis_labels[0], +# endif + VMMDEV_MOUSE_RANGE_MIN /* min X */, VMMDEV_MOUSE_RANGE_MAX /* max X */, + 10000, 0, 10000 +# if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12 + , Absolute +# endif + ); + + xf86InitValuatorAxisStruct(device, 1, +# if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + axis_labels[1], +# endif + VMMDEV_MOUSE_RANGE_MIN /* min Y */, VMMDEV_MOUSE_RANGE_MAX /* max Y */, + 10000, 0, 10000 +# if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12 + , Absolute +# endif + ); +#endif + xf86InitValuatorDefaults(device, 0); + xf86InitValuatorDefaults(device, 1); + xf86MotionHistoryAllocate(device->public.devicePrivate); + + return Success; +} + +static int +VBoxProc(DeviceIntPtr device, int what) +{ + InputInfoPtr pInfo; + int rc, xrc; + uint32_t fFeatures = 0; + + pInfo = device->public.devicePrivate; + + switch (what) + { + case DEVICE_INIT: + xrc = VBoxInit(device); + if (xrc != Success) { + VbglR3Term(); + return xrc; + } + break; + + case DEVICE_ON: + xf86Msg(X_INFO, "%s: On.\n", pInfo->name); + if (device->public.on) + break; + /* Tell the host that we want absolute co-ordinates */ + rc = VbglR3GetMouseStatus(&fFeatures, NULL, NULL); + fFeatures &= VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR; + if (RT_SUCCESS(rc)) + rc = VbglR3SetMouseStatus( fFeatures + | VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE + | VMMDEV_MOUSE_NEW_PROTOCOL); + if (!RT_SUCCESS(rc)) { + xf86Msg(X_ERROR, "%s: Failed to switch guest mouse into absolute mode\n", + pInfo->name); + return !Success; + } + + xf86AddEnabledDevice(pInfo); + device->public.on = TRUE; + break; + + case DEVICE_OFF: + xf86Msg(X_INFO, "%s: Off.\n", pInfo->name); + rc = VbglR3GetMouseStatus(&fFeatures, NULL, NULL); + fFeatures &= VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR; + if (RT_SUCCESS(rc)) + rc = VbglR3SetMouseStatus( fFeatures + & ~VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE + & ~VMMDEV_MOUSE_NEW_PROTOCOL); + xf86RemoveEnabledDevice(pInfo); + device->public.on = FALSE; + break; + + case DEVICE_CLOSE: + VbglR3Term(); + xf86Msg(X_INFO, "%s: Close\n", pInfo->name); + break; + + default: + return BadValue; + } + + return Success; +} + +static int +VBoxProbe(InputInfoPtr pInfo) +{ + int rc = VbglR3Init(); + if (!RT_SUCCESS(rc)) { + xf86Msg(X_ERROR, "%s: Failed to open the VirtualBox device (error %d)\n", + pInfo->name, rc); + return BadMatch; + } + + return Success; +} + +static Bool +VBoxConvert(InputInfoPtr pInfo, int first, int num, int v0, int v1, int v2, + int v3, int v4, int v5, int *x, int *y) +{ + RT_NOREF(pInfo, num, v2, v3, v4, v5); + + if (first == 0) { + *x = xf86ScaleAxis(v0, 0, screenInfo.screens[0]->width, 0, 65536); + *y = xf86ScaleAxis(v1, 0, screenInfo.screens[0]->height, 0, 65536); + return TRUE; + } + return FALSE; +} + +static int +VBoxPreInitInfo(InputDriverPtr drv, InputInfoPtr pInfo, int flags) +{ + const char *device; + int rc; + RT_NOREF(drv, flags); + + /* Initialise the InputInfoRec. */ + pInfo->device_control = VBoxProc; + pInfo->read_input = VBoxReadInput; + /* Unlike evdev, we set this unconditionally, as we don't handle keyboards. */ + pInfo->type_name = XI_MOUSE; + pInfo->flags |= XI86_ALWAYS_CORE; + + device = xf86SetStrOption(pInfo->options, "Device", + "/dev/vboxguest"); + + xf86Msg(X_CONFIG, "%s: Device: \"%s\"\n", pInfo->name, device); + do { + pInfo->fd = open(device, O_RDWR, 0); + } + while (pInfo->fd < 0 && errno == EINTR); + + if (pInfo->fd < 0) { + xf86Msg(X_ERROR, "Unable to open VirtualBox device \"%s\".\n", device); + return BadMatch; + } + + rc = VBoxProbe(pInfo); + if (rc != Success) + return rc; + + return Success; +} + +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 12 +static InputInfoPtr +VBoxPreInit(InputDriverPtr drv, IDevPtr dev, int flags) +{ + InputInfoPtr pInfo = xf86AllocateInput(drv, 0); + if (!pInfo) + return NULL; + + /* Initialise the InputInfoRec. */ + pInfo->name = dev->identifier; + pInfo->conf_idev = dev; + pInfo->conversion_proc = VBoxConvert; + pInfo->flags = XI86_POINTER_CAPABLE | XI86_SEND_DRAG_EVENTS; + + xf86CollectInputOptions(pInfo, NULL, NULL); + xf86ProcessCommonOptions(pInfo, pInfo->options); + + if (VBoxPreInitInfo(drv, pInfo, flags) != Success) { + xf86DeleteInput(pInfo, 0); + return NULL; + } + + pInfo->flags |= XI86_CONFIGURED; + return pInfo; +} +#endif + +_X_EXPORT InputDriverRec VBOXMOUSE = { + 1, + "vboxmouse", + NULL, +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 12 + VBoxPreInit, +#else + VBoxPreInitInfo, +#endif + NULL, + NULL, + 0 +}; + +static pointer +VBoxPlug(pointer module, pointer options, int *errmaj, int *errmin) +{ + RT_NOREF(options, errmaj, errmin); + xf86AddInputDriver(&VBOXMOUSE, module, 0); + xf86Msg(X_CONFIG, "Load address of symbol \"VBOXMOUSE\" is %p\n", + (void *)&VBOXMOUSE); + return module; +} + +static XF86ModuleVersionInfo VBoxVersionRec = +{ + "vboxmouse", + VBOX_VENDOR, + MODINFOSTRING1, + MODINFOSTRING2, + 0, /* Missing from SDK: XORG_VERSION_CURRENT, */ + 1, 0, 0, + ABI_CLASS_XINPUT, + ABI_XINPUT_VERSION, + MOD_CLASS_XINPUT, + {0, 0, 0, 0} +}; + +_X_EXPORT XF86ModuleData vboxmouseModuleData = +{ + &VBoxVersionRec, + VBoxPlug, + NULL +}; diff --git a/src/VBox/Additions/x11/vboxvideo/HGSMIMemAlloc.h b/src/VBox/Additions/x11/vboxvideo/HGSMIMemAlloc.h new file mode 100644 index 00000000..572ccc80 --- /dev/null +++ b/src/VBox/Additions/x11/vboxvideo/HGSMIMemAlloc.h @@ -0,0 +1,63 @@ +/* $Id: HGSMIMemAlloc.h $ */ +/* + * Copyright (C) 2017-2019 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/* In builds inside of the VirtualBox source tree we override the default + * HGSMIMemAlloc.h using -include, therefore this define must match the one + * there. */ + +#ifndef VBOX_INCLUDED_Graphics_HGSMIMemAlloc_h +#define VBOX_INCLUDED_Graphics_HGSMIMemAlloc_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include "HGSMIDefs.h" +#include "VBoxVideoIPRT.h" + +#define HGSMI_MA_DESC_ORDER_BASE UINT32_C(5) + +#define HGSMI_MA_BLOCK_SIZE_MIN (UINT32_C(1) << (HGSMI_MA_DESC_ORDER_BASE + 0)) + +typedef struct HGSMIMADATA +{ + HGSMIAREA area; + bool fAllocated; +} HGSMIMADATA; + +RT_C_DECLS_BEGIN + +int HGSMIMAInit(HGSMIMADATA *pMA, const HGSMIAREA *pArea, + HGSMIOFFSET *paDescriptors, uint32_t cDescriptors, + HGSMISIZE cbMaxBlock, const HGSMIENV *pEnv); +void HGSMIMAUninit(HGSMIMADATA *pMA); + +void RT_UNTRUSTED_VOLATILE_GUEST *HGSMIMAAlloc(HGSMIMADATA *pMA, HGSMISIZE cb); +void HGSMIMAFree(HGSMIMADATA *pMA, void RT_UNTRUSTED_VOLATILE_GUEST *pv); + +RT_C_DECLS_END + +#endif /* !VBOX_INCLUDED_Graphics_HGSMIMemAlloc_h */ diff --git a/src/VBox/Additions/x11/vboxvideo/Makefile.kmk b/src/VBox/Additions/x11/vboxvideo/Makefile.kmk new file mode 100644 index 00000000..98983c5e --- /dev/null +++ b/src/VBox/Additions/x11/vboxvideo/Makefile.kmk @@ -0,0 +1,458 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the VBox Linux Additions X.org graphics driver. +# + +# +# Copyright (C) 2006-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# + +SUB_DEPTH = ../../../../.. +include $(KBUILD_PATH)/subheader.kmk + +vboxvideo_70_DEFS := \ + IN_MODULE XORG_7X RENDER=1 IN_RT_STATIC X_BYTE_ORDER=X_LITTLE_ENDIAN +ifeq ($(KBUILD_TARGET),solaris) # don't use .solaris or anything here. + vboxvideo_70_DEFS += __EXTENSIONS__ ## @todo Why this? +endif +vboxvideo_13_DEFS := $(vboxvideo_70_DEFS) VBOXVIDEO_13 +vboxvideo_15_DEFS := \ + $(vboxvideo_13_DEFS) NO_ANSIC PCIACCESS XSERVER_LIBPCIACCESS _XORG_SERVER_H_ _DIX_CONFIG_H_ +vboxvideo_xorg_INCS = \ + $(VBOX_PATH_X11_ROOT)/fontsproto-2.1.0 \ + $(VBOX_PATH_X11_ROOT)/inputproto-1.9.99.902 \ + $(VBOX_PATH_X11_ROOT)/kbproto-1.0.6 \ + $(VBOX_PATH_X11_ROOT)/libpciaccess-0.10.8 \ + $(VBOX_PATH_X11_ROOT)/libXext-1.3.1 \ + $(VBOX_PATH_X11_ROOT)/pixman-0.16.0 \ + $(VBOX_PATH_X11_ROOT)/randrproto-1.3.0 \ + $(VBOX_PATH_X11_ROOT)/renderproto-0.11 \ + $(VBOX_PATH_X11_ROOT)/xextproto-7.1.1 \ + $(VBOX_PATH_X11_ROOT)/xproto-7.0.18 \ + $(VBOX_GRAPHICS_INCS) +vboxvideo_override_INCLUDES = \ + -include $(PATH_ROOT)/src/VBox/Additions/x11/vboxvideo/VBoxVideoIPRT.h \ + -include $(PATH_ROOT)/src/VBox/Additions/x11/vboxvideo/HGSMIMemAlloc.h + +LIBRARIES += vboxvideo_drv_lib + +# +# vboxvideo_drv_lib +# +vboxvideo_drv_lib_TEMPLATE = VBOXGUESTR3XORGMOD +# We are relying in the include guard in the two headers below to stop the more +# generic ones from being included. Not very nice, I know. +vboxvideo_drv_lib_CFLAGS += $(vboxvideo_override_INCLUDES) +vboxvideo_drv_lib_CXXFLAGS += $(vboxvideo_override_INCLUDES) +ifeq ($(KBUILD_TARGET),solaris) # don't use .solaris or anything here. Do we need this? I don't want to find out. + vboxvideo_drv_lib_CFLAGS += -D_XPG6 -Wno-shadow # Use XPG6 until we have moved the C++ bits into a library. +endif +vboxvideo_drv_lib_SOURCES = \ + $(PATH_ROOT)/src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp \ + $(PATH_ROOT)/src/VBox/Additions/common/VBoxVideo/HGSMIBuffers.cpp \ + $(PATH_ROOT)/src/VBox/Additions/common/VBoxVideo/Modesetting.cpp \ + $(PATH_ROOT)/src/VBox/Additions/common/VBoxVideo/VBVABase.cpp \ + $(PATH_ROOT)/src/VBox/GuestHost/HGSMI/HGSMICommon.cpp \ + $(PATH_ROOT)/src/VBox/Additions/x11/vboxvideo/hgsmimemalloc.c +# $(VBOX_PATH_X11_ROOT)/xorg-server-1.18.0 is for in[blw] and out[blw], xproto +# for _X_[UN]LIKELY. +vboxvideo_drv_lib_INCS = \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.18.0 \ + $(VBOX_PATH_X11_ROOT)/xproto-7.0.18 \ + $(VBOX_PATH_X11_ROOT)/pixman-0.16.0 \ + $(PATH_ROOT)/src/VBox/Runtime/include \ + $(VBOX_GRAPHICS_INCS) +vboxvideo_drv_lib_INST = $(INST_LIB) + +# +# vboxvideo_drv +# +if1of ($(KBUILD_TARGET), linux) + SYSMODS += vboxvideo_drv +endif # target linux +vboxvideo_drv_TEMPLATE = VBOXGUESTR3XF86MOD +vboxvideo_drv_CFLAGS += $(vboxvideo_override_INCLUDES) +vboxvideo_drv_CFLAGS += -Wno-shadow # Avoid MBs of warnings in X11 and OpenGL headers (solaris mostly). +vboxvideo_drv_CXXFLAGS += $(vboxvideo_override_INCLUDES) +vboxvideo_drv_DEFS.linux = linux +vboxvideo_drv_DEFS.x86 = __i386__ +# This one has to be defined when building server code on systems where +# unsigned long is 64bits +vboxvideo_drv_DEFS.amd64 += _XSERVER64 +vboxvideo_drv_DEFS = \ + _POSIX_C_SOURCE=199309L _POSIX_SOURCE _XOPEN_SOURCE _DEFAULT_SOURCE \ + _BSD_SOURCE _SVID_SOURCE _GNU_SOURCE SHAPE XINPUT XKB LBX XAPPGROUP \ + XCSECURITY TOGCUP XF86BIGFONT DPMSExtension PIXPRIV PANORAMIX RENDER \ + GCCUSESGAS AVOID_GLYPHBLT PIXPRIV SINGLEDEPTH XFreeXDGA XvExtension \ + XFree86LOADER XFree86Server XF86VIDMODE XvMCExtension SMART_SCHEDULE \ + BUILDDEBUG X_BYTE_ORDER=X_LITTLE_ENDIAN DNDEBUG FUNCPROTO=15 NARROWPROTO \ + IN_MODULE XFree86Module IN_XF86_MODULE IN_RT_STATIC +vboxvideo_drv_INCS = \ + $(VBOX_PATH_X11_ROOT)/XFree86-4.3 \ + $(VBOX_PATH_X11_ROOT)/XFree86-4.3/X11 \ + $(VBOX_PATH_X11_ROOT)/XFree86-4.3/X11/extensions \ + $(VBOX_PATH_X11_ROOT)/XFree86-4.3/X11/fonts \ + $(VBOX_PATH_X11_ROOT)/XFree86-4.3/Xserver +vboxvideo_drv_INCS += \ + $(PATH_ROOT)/src/VBox/Runtime/include \ + $(VBOX_GRAPHICS_INCS) +vboxvideo_drv_SOURCES = \ + getmode.c \ + pointer.c \ + setmode.c \ + vboxvideo.c \ + vbva.c \ + $(vboxvideo_drv_lib_SOURCES) + # Any global symbols in the driver object files will be added to XFree86's + # symbol table, which can cause problems if we e.g. define a symbol in two + # modules. + vboxvideo_drv_POST_CMDS = \ + objcopy --keep-global-symbol vboxvideoModuleData $(out) $(out)-objcopy$$(NLTAB) \ + $(MV) -f $(out)-objcopy $(out) + +# +# vboxvideo_drv_70 +# +# Remark: The other X.org drivers below are derived from this one. So, to make +# that as simple as possible we do ifeq/if1of test here and extends the +# base keywords instead of using .solaris or .linux. +# Also it is *important* to use := and not = when deriving a property. +# +DLLS += vboxvideo_drv_70 +vboxvideo_drv_70_TEMPLATE = VBOXGUESTR3XORGMOD +vboxvideo_drv_70_DEFS = $(vboxvideo_70_DEFS) XORG_VERSION_CURRENT=70000000 +vboxvideo_drv_70_CFLAGS += $(vboxvideo_override_INCLUDES) +ifeq ($(KBUILD_TARGET),solaris) # don't use .solaris or anything here. + vboxvideo_drv_70_CFLAGS += -D_XPG6 -Wno-shadow # Use XPG6 until we have moved the C++ bits into a library. +endif +vboxvideo_drv_70_INCS = \ + $(vboxvideo_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.0.1 +vboxvideo_drv_70_INCS += $(PATH_ROOT)/src/VBox/Runtime/include +vboxvideo_drv_70_SOURCES = $(filter-out $(vboxvideo_drv_lib_SOURCES),$(vboxvideo_drv_SOURCES)) +vboxvideo_drv_70_LIBS = $(PATH_STAGE_LIB)/vboxvideo_drv_lib$(VBOX_SUFF_LIB) + + +# +# vboxvideo_drv_71 +# +DLLS += vboxvideo_drv_71 +vboxvideo_drv_71_TEMPLATE = VBOXGUESTR3XORGMOD +vboxvideo_drv_71_CFLAGS := $(vboxvideo_drv_70_CFLAGS) +vboxvideo_drv_71_DEFS := $(vboxvideo_70_DEFS) XORG_VERSION_CURRENT=70100000 +vboxvideo_drv_71_INCS = \ + $(vboxvideo_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.1.0 +vboxvideo_drv_71_INCS += $(PATH_ROOT)/src/VBox/Runtime/include +vboxvideo_drv_71_SOURCES = $(vboxvideo_drv_70_SOURCES) +vboxvideo_drv_71_LIBS = $(vboxvideo_drv_70_LIBS) + + +# +# vboxvideo_drv_13 +# +DLLS += vboxvideo_drv_13 +vboxvideo_drv_13_TEMPLATE = VBOXGUESTR3XORGMOD +vboxvideo_drv_13_CFLAGS := $(vboxvideo_drv_70_CFLAGS) +vboxvideo_drv_13_DEFS := $(vboxvideo_13_DEFS) XORG_VERSION_CURRENT=10300000 +vboxvideo_drv_13_INCS = \ + $(vboxvideo_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.3.0.0 +vboxvideo_drv_13_INCS += $(PATH_ROOT)/src/VBox/Runtime/include +vboxvideo_drv_13_SOURCES = $(vboxvideo_drv_70_SOURCES) edid.c +vboxvideo_drv_13_LIBS += $(vboxvideo_drv_70_LIBS) + + +# +# vboxvideo_drv_14 +# +DLLS += vboxvideo_drv_14 +vboxvideo_drv_14_TEMPLATE = VBOXGUESTR3XORGMOD +vboxvideo_drv_14_CFLAGS := $(vboxvideo_drv_70_CFLAGS) +vboxvideo_drv_14_DEFS := $(vboxvideo_13_DEFS) XORG_VERSION_CURRENT=10400000 +vboxvideo_drv_14_INCS = \ + $(vboxvideo_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.4.2 +vboxvideo_drv_14_INCS += $(PATH_ROOT)/src/VBox/Runtime/include +vboxvideo_drv_14_SOURCES = $(vboxvideo_drv_13_SOURCES) +vboxvideo_drv_14_LIBS += $(vboxvideo_drv_70_LIBS) + + +# +# vboxvideo_drv_15 +# +DLLS += vboxvideo_drv_15 +vboxvideo_drv_15_TEMPLATE = VBOXGUESTR3XORGMOD +vboxvideo_drv_15_CFLAGS := $(vboxvideo_drv_70_CFLAGS) +vboxvideo_drv_15_DEFS := $(vboxvideo_15_DEFS) XORG_VERSION_CURRENT=10503000 +vboxvideo_drv_15_INCS = \ + $(vboxvideo_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.5.3 +vboxvideo_drv_15_INCS += $(PATH_ROOT)/src/VBox/Runtime/include +vboxvideo_drv_15_SOURCES = $(vboxvideo_drv_13_SOURCES) +vboxvideo_drv_15_LIBS += $(vboxvideo_drv_70_LIBS) + + +# +# vboxvideo_drv_16 +# +DLLS += vboxvideo_drv_16 +vboxvideo_drv_16_TEMPLATE = VBOXGUESTR3XORGMOD +vboxvideo_drv_16_CFLAGS := $(vboxvideo_drv_70_CFLAGS) +vboxvideo_drv_16_DEFS := $(vboxvideo_15_DEFS) XORG_VERSION_CURRENT=10600000 +vboxvideo_drv_16_INCS = \ + $(vboxvideo_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.6.5 \ +vboxvideo_drv_16_INCS += $(PATH_ROOT)/src/VBox/Runtime/include +vboxvideo_drv_16_SOURCES := $(vboxvideo_drv_15_SOURCES) +vboxvideo_drv_16_LIBS += $(vboxvideo_drv_70_LIBS) + + +# +# vboxvideo_drv_17 +# +DLLS += vboxvideo_drv_17 +vboxvideo_drv_17_TEMPLATE = VBOXGUESTR3XORGMOD +vboxvideo_drv_17_CFLAGS := $(vboxvideo_drv_70_CFLAGS) +vboxvideo_drv_17_DEFS := $(vboxvideo_15_DEFS) XORG_VERSION_CURRENT=10699000 +vboxvideo_drv_17_INCS = \ + $(vboxvideo_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.7.7 +vboxvideo_drv_17_INCS += $(PATH_ROOT)/src/VBox/Runtime/include +vboxvideo_drv_17_SOURCES := $(vboxvideo_drv_13_SOURCES) +vboxvideo_drv_17_LIBS += $(vboxvideo_drv_70_LIBS) + + +# +# vboxvideo_drv_18 +# +DLLS += vboxvideo_drv_18 +vboxvideo_drv_18_TEMPLATE = VBOXGUESTR3XORGMOD +vboxvideo_drv_18_CFLAGS := $(vboxvideo_drv_70_CFLAGS) +vboxvideo_drv_18_DEFS := $(vboxvideo_15_DEFS) XORG_VERSION_CURRENT=10800000 +vboxvideo_drv_18_INCS = \ + $(vboxvideo_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.8.0 +vboxvideo_drv_18_INCS += $(PATH_ROOT)/src/VBox/Runtime/include +vboxvideo_drv_18_SOURCES := $(vboxvideo_drv_17_SOURCES) +vboxvideo_drv_18_LIBS += $(vboxvideo_drv_70_LIBS) + + +# +# vboxvideo_drv_19 +# +DLLS += vboxvideo_drv_19 +vboxvideo_drv_19_TEMPLATE = VBOXGUESTR3XORGMOD +vboxvideo_drv_19_CFLAGS := $(vboxvideo_drv_70_CFLAGS) +vboxvideo_drv_19_DEFS := $(vboxvideo_15_DEFS) XORG_VERSION_CURRENT=10900000 +vboxvideo_drv_19_INCS = \ + $(vboxvideo_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.9.0 +vboxvideo_drv_19_INCS += $(PATH_ROOT)/src/VBox/Runtime/include +vboxvideo_drv_19_SOURCES := $(vboxvideo_drv_17_SOURCES) +vboxvideo_drv_19_LIBS += $(vboxvideo_drv_70_LIBS) + + +# +# vboxvideo_drv_110 +# +DLLS += vboxvideo_drv_110 +vboxvideo_drv_110_TEMPLATE = VBOXGUESTR3XORGMOD +vboxvideo_drv_110_CFLAGS := $(vboxvideo_drv_70_CFLAGS) +vboxvideo_drv_110_DEFS := $(vboxvideo_15_DEFS) XORG_VERSION_CURRENT=11000000 +vboxvideo_drv_110_INCS = \ + $(vboxvideo_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.10.0 +vboxvideo_drv_110_INCS += $(PATH_ROOT)/src/VBox/Runtime/include +vboxvideo_drv_110_SOURCES := $(vboxvideo_drv_17_SOURCES) +vboxvideo_drv_110_LIBS += $(vboxvideo_drv_70_LIBS) + + +# +# vboxvideo_drv_111 +# +DLLS += vboxvideo_drv_111 +vboxvideo_drv_111_TEMPLATE = VBOXGUESTR3XORGMOD +vboxvideo_drv_111_CFLAGS := $(vboxvideo_drv_70_CFLAGS) +vboxvideo_drv_111_DEFS := $(vboxvideo_15_DEFS) XORG_VERSION_CURRENT=11100000 +vboxvideo_drv_111_INCS = \ + $(vboxvideo_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.11.0 +vboxvideo_drv_111_INCS += $(PATH_ROOT)/src/VBox/Runtime/include +vboxvideo_drv_111_SOURCES := $(vboxvideo_drv_17_SOURCES) +vboxvideo_drv_111_LIBS += $(vboxvideo_drv_70_LIBS) + + +# +# vboxvideo_drv_112 +# +DLLS += vboxvideo_drv_112 +vboxvideo_drv_112_TEMPLATE = VBOXGUESTR3XORGMOD +vboxvideo_drv_112_CFLAGS := $(vboxvideo_drv_70_CFLAGS) +vboxvideo_drv_112_DEFS := $(vboxvideo_15_DEFS) XORG_VERSION_CURRENT=11200000 +vboxvideo_drv_112_INCS = \ + $(vboxvideo_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.12.0 +vboxvideo_drv_112_INCS += $(PATH_ROOT)/src/VBox/Runtime/include +vboxvideo_drv_112_SOURCES := $(vboxvideo_drv_17_SOURCES) +vboxvideo_drv_112_LIBS += $(vboxvideo_drv_70_LIBS) + + +# +# vboxvideo_drv_113 +# +DLLS += vboxvideo_drv_113 +vboxvideo_drv_113_TEMPLATE = VBOXGUESTR3XORGMOD +vboxvideo_drv_113_CFLAGS := $(vboxvideo_drv_70_CFLAGS) +vboxvideo_drv_113_DEFS := $(vboxvideo_15_DEFS) XORG_VERSION_CURRENT=11300000 +vboxvideo_drv_113_INCS = \ + $(vboxvideo_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.13.0 +vboxvideo_drv_113_INCS += $(PATH_ROOT)/src/VBox/Runtime/include +vboxvideo_drv_113_SOURCES := $(vboxvideo_drv_17_SOURCES) +vboxvideo_drv_113_LIBS += $(vboxvideo_drv_70_LIBS) + + +# +# vboxvideo_drv_114 +# +DLLS += vboxvideo_drv_114 +vboxvideo_drv_114_TEMPLATE = VBOXGUESTR3XORGMOD +vboxvideo_drv_114_CFLAGS := $(vboxvideo_drv_70_CFLAGS) +vboxvideo_drv_114_DEFS := $(vboxvideo_15_DEFS) XORG_VERSION_CURRENT=11400000 +vboxvideo_drv_114_INCS = \ + $(vboxvideo_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.14.0 +vboxvideo_drv_114_INCS += $(PATH_ROOT)/src/VBox/Runtime/include +vboxvideo_drv_114_SOURCES := $(vboxvideo_drv_17_SOURCES) +vboxvideo_drv_114_LIBS += $(vboxvideo_drv_70_LIBS) + + +# +# vboxvideo_drv_115 +# +DLLS += vboxvideo_drv_115 +vboxvideo_drv_115_TEMPLATE = VBOXGUESTR3XORGMOD +vboxvideo_drv_115_CFLAGS := $(vboxvideo_drv_70_CFLAGS) +vboxvideo_drv_115_DEFS := $(vboxvideo_15_DEFS) XORG_VERSION_CURRENT=11500000 +vboxvideo_drv_115_INCS = \ + $(vboxvideo_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.15.0 +vboxvideo_drv_115_INCS += $(PATH_ROOT)/src/VBox/Runtime/include +vboxvideo_drv_115_SOURCES := $(vboxvideo_drv_17_SOURCES) +vboxvideo_drv_115_LIBS += $(vboxvideo_drv_70_LIBS) + + +# +# vboxvideo_drv_116 +# +DLLS += vboxvideo_drv_116 +vboxvideo_drv_116_TEMPLATE = VBOXGUESTR3XORGMOD +vboxvideo_drv_116_CFLAGS := $(vboxvideo_drv_70_CFLAGS) +vboxvideo_drv_116_DEFS := $(vboxvideo_15_DEFS) XORG_VERSION_CURRENT=11600000 +vboxvideo_drv_116_INCS = \ + $(vboxvideo_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.16.0 +vboxvideo_drv_116_INCS += $(PATH_ROOT)/src/VBox/Runtime/include +vboxvideo_drv_116_SOURCES := $(vboxvideo_drv_17_SOURCES) +vboxvideo_drv_116_LIBS += $(vboxvideo_drv_70_LIBS) + + +# +# vboxvideo_drv_117 +# +DLLS += vboxvideo_drv_117 +vboxvideo_drv_117_TEMPLATE = VBOXGUESTR3XORGMOD +vboxvideo_drv_117_CFLAGS := $(vboxvideo_drv_70_CFLAGS) +vboxvideo_drv_117_DEFS := $(vboxvideo_15_DEFS) XORG_VERSION_CURRENT=11700000 +vboxvideo_drv_117_INCS = \ + $(vboxvideo_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.17.1 +vboxvideo_drv_117_INCS += $(PATH_ROOT)/src/VBox/Runtime/include +vboxvideo_drv_117_SOURCES := $(vboxvideo_drv_17_SOURCES) +vboxvideo_drv_117_LIBS += $(vboxvideo_drv_70_LIBS) + + +# +# vboxvideo_drv_118 +# +DLLS += vboxvideo_drv_118 +vboxvideo_drv_118_TEMPLATE = VBOXGUESTR3XORGMOD +vboxvideo_drv_118_CFLAGS := $(vboxvideo_drv_70_CFLAGS) +vboxvideo_drv_118_DEFS := $(vboxvideo_15_DEFS) XORG_VERSION_CURRENT=11800000 +vboxvideo_drv_118_INCS = \ + $(vboxvideo_xorg_INCS) \ + $(VBOX_PATH_X11_ROOT)/xorg-server-1.18.0 +vboxvideo_drv_118_INCS += $(PATH_ROOT)/src/VBox/Runtime/include +vboxvideo_drv_118_SOURCES := $(vboxvideo_drv_17_SOURCES) +vboxvideo_drv_118_LIBS += $(vboxvideo_drv_70_LIBS) + +ifdef VBOX_USE_SYSTEM_XORG_HEADERS + # Build using local X.Org headers. We assume X.Org Server 1.7 or later. + DLLS := $(filter-out vboxvideo_drv_%,$(DLLS)) vboxvideo_drv_system + SYSMODS := $(filter-out vboxvideo_drv%,$(SYSMODS)) + vboxvideo_drv_system_TEMPLATE = VBOXGUESTR3XORGMOD + vboxvideo_drv_system_CFLAGS := \ + $(vboxvideo_drv_70_CFLAGS) -include xorg-server.h + vboxvideo_drv_system_DEFS := $(filter-out _XORG_SERVER_H_ _DIX_CONFIG_H_, $(vboxvideo_15_DEFS)) + vboxvideo_drv_system_INCS += \ + $(PATH_ROOT)/src/VBox/Runtime/include \ + $(VBOX_GRAPHICS_INCS) \ + /usr/include/xorg \ + /usr/include/pixman-1 + vboxvideo_drv_system_SOURCES := $(vboxvideo_drv_17_SOURCES) +endif + + +# Check the undefined symbols in the X.Org modules against lists of allowed +# symbols. Not very elegant, but it will catch problems early. + +ifdef VBOX_WITH_TESTCASES +# ifndef VBOX_ONLY_ADDITIONS + ifndef VBOX_USE_SYSTEM_XORG_HEADERS + if1of ($(KBUILD_TARGET),linux solaris) + ifeq ($(KBUILD_HOST_ARCH),$(KBUILD_TARGET_ARCH)) + ifndef VBOX_ONLY_SDK + VBOXVIDEO_SRC_PATH := $(PATH_SUB_CURRENT) + + ifeq ($(KBUILD_TARGET),linux) + TESTING += $(vboxvideo_drv_0_OUTDIR)/tstvboxvideo68.run + OTHERS += $(vboxvideo_drv_0_OUTDIR)/tstvboxvideo68.run +$$(vboxvideo_drv_0_OUTDIR)/tstvboxvideo68.run: $$(vboxvideo_drv_1_STAGE_TARGET) + $(QUIET)$(call MSG_L1,Checking for unresolved symbols in $<) + $(QUIET)$(ASH) $(PATH_ROOT)/src/bldprogs/checkUndefined.sh $(KBUILD_HOST) \ + $(vboxvideo_drv_1_STAGE_TARGET) --static $(VBOXVIDEO_SRC_PATH)/../undefined_xfree86 $(VBOXVIDEO_SRC_PATH)/../undefined_xfree86_modules + $(QUIET)$(APPEND) -t "$@" "done" + endif + +## +# Using the extra expansion to replace $(ver) before eval, thus everything +# else needs escaped dollars. + define def_vboxvideo_test + TESTING += $$(vboxvideo_drv$(ver)_0_OUTDIR)/tstvboxvideo$(ver).run + OTHERS += $$(vboxvideo_drv$(ver)_0_OUTDIR)/tstvboxvideo$(ver).run + $$$$(vboxvideo_drv$(ver)_0_OUTDIR)/tstvboxvideo$(ver).run: $$$$(vboxvideo_drv$(ver)_1_STAGE_TARGET) + $$(QUIET)$$(call MSG_L1,Checking for unresolved symbols in $$<) + $$(QUIET)$$(ASH) $$(PATH_ROOT)/src/bldprogs/checkUndefined.sh $$(KBUILD_HOST) \ + $$(vboxvideo_drv$(ver)_1_STAGE_TARGET) $$(VBOXVIDEO_SRC_PATH)/../undefined_xfree86 $(VBOXVIDEO_SRC_PATH)/../undefined_xfree86_modules $$(VBOXVIDEO_SRC_PATH)/../undefined_xorg + $$(QUIET)$$(APPEND) -t "$$@" "done" + endef + + $(foreach ver, _70 _71 _13 _14 _15 _16 _17 _18 _19 _110 _111 _112 _113 _114 _115 _116 _117 _118, $(eval $(def_vboxvideo_test))) + + endif # ! VBOX_ONLY_SDK + endif # eq ($(KBUILD_HOST_ARCH),$(KBUILD_TARGET_ARCH)) + endif # 1of ($(KBUILD_TARGET),linux solaris) + endif # ! VBOX_USE_SYSTEM_XORG_HEADERS +# endif # ! VBOX_ONLY_ADDITIONS +endif # VBOX_WITH_TESTCASES + +include $(FILE_KBUILD_SUB_FOOTER) diff --git a/src/VBox/Additions/x11/vboxvideo/README.testing b/src/VBox/Additions/x11/vboxvideo/README.testing new file mode 100644 index 00000000..03d6482f --- /dev/null +++ b/src/VBox/Additions/x11/vboxvideo/README.testing @@ -0,0 +1,33 @@ +This file contains some notes about things to try out to give the X.Org video +driver a reasonably thorough test. We will add cases of things which have been +known to fail in the past to this file as we discover them. Tests should be +carried out with Additions installed, and both with and without 3D enabled in +the machine settings. + + * Test XFree86 guests (CentOS 3), early X.Org (CentOS 5) and recent + (CentOS 6 and 7, current Ubuntu/Fedora). Test Solaris guests (10 and 11?). + * Dynamic resizing should work, on CentOS 6 and later Linux guests it should + work without VBoxClient running. + * Disabling and enabling virtual screens (VBoxManage in 4.3). + * Dynamic resizing with one of more virtual screens disabled. + * Test switching to virtual terminals and back from windowed, full screen and + seamless modes (seamless currently only works properly with VBoxClient + running). + * Test switching directly between normal, full-screen, seamless and scaled + modes. + * Test re-ordering the virtual screen using the native guest operating system + tools and make sure that mouse integration still works as expected. + * Test disabling and re-enabling guest screens with the native system tools. + * Try disabling and re-enabling mouse integration and check that capturing + works with multiple guest screens. + * Shutting down and re-starting a virtual machine should restore the last size + for all monitors (note: currently only after log-in). Full shut-down, not + a reboot. + * Test power management by disabling guest screens ("xrandr --output VGA-n + --off") and re-enabling them ("xrandr --output VGA-n --preferred --pos XxY") + where X and Y are the position of the screen before disabling it. + * Test sending video mode hints with screen position information via + VBoxManage. The screen position is a hint only. The approximate position + should be preserved after a shut down and re-start of the guest. + * Test re-starting the X server after resizing all guest windows. The server + should not crash. diff --git a/src/VBox/Additions/x11/vboxvideo/VBoxVideoIPRT.h b/src/VBox/Additions/x11/vboxvideo/VBoxVideoIPRT.h new file mode 100644 index 00000000..4c1b85e1 --- /dev/null +++ b/src/VBox/Additions/x11/vboxvideo/VBoxVideoIPRT.h @@ -0,0 +1,237 @@ +/* $Id: VBoxVideoIPRT.h $ */ +/* + * Copyright (C) 2017-2019 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/* In builds inside of the VirtualBox source tree we override the default + * VBoxVideoIPRT.h using -include, therefore this define must match the one + * there. */ + +#ifndef VBOX_INCLUDED_Graphics_VBoxVideoIPRT_h +#define VBOX_INCLUDED_Graphics_VBoxVideoIPRT_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +# include "VBoxVideoErr.h" + +#ifndef __cplusplus +typedef enum +{ + false = 0, + true +} bool; +# define RT_C_DECLS_BEGIN +# define RT_C_DECLS_END +#else +# define RT_C_DECLS_BEGIN extern "C" { +# define RT_C_DECLS_END } +#endif + +#if defined(IN_XF86_MODULE) && !defined(NO_ANSIC) +# ifdef __cplusplus +/* xf86Module.h redefines this. */ +# define NULL 0 +# endif +RT_C_DECLS_BEGIN +# include "xf86_ansic.h" +RT_C_DECLS_END +#endif /* defined(IN_XF86_MODULE) && !defined(NO_ANSIC) */ +#define __STDC_LIMIT_MACROS /* define *INT*_MAX on C++ too. */ +#include "compiler.h" /* Can pull in <sdtint.h>. Must come after xf86_ansic.h on XFree86. */ +#include <X11/Xfuncproto.h> +#include <stdint.h> +#if defined(IN_XF86_MODULE) && !defined(NO_ANSIC) +/* XFree86 did not have these. Not that I care much for micro-optimisations + * in most cases anyway. */ +# define _X_LIKELY(x) (x) +# define _X_UNLIKELY(x) (x) +# ifndef offsetof +# define offsetof(type, member) ( (int)(uintptr_t)&( ((type *)(void *)0)->member) ) +# endif +#else /* !(defined(IN_XF86_MODULE) && !defined(NO_ANSIC)) */ +# include <stdarg.h> +# include <stddef.h> +# include <string.h> +#endif /* !(defined(IN_XF86_MODULE) && !defined(NO_ANSIC)) */ + +RT_C_DECLS_BEGIN +extern int RTASSERTVAR[1]; +RT_C_DECLS_END + +#define AssertCompile(expr) \ + extern int RTASSERTVAR[1] __attribute__((__unused__)), \ + RTASSERTVAR[(expr) ? 1 : 0] __attribute__((__unused__)) +#define AssertCompileSize(type, size) \ + AssertCompile(sizeof(type) == (size)) +#define AssertPtrNullReturnVoid(a) do { } while(0) + +#if !defined(IN_XF86_MODULE) && defined(DEBUG) +# include <assert.h> +# define Assert assert +# define AssertFailed() assert(0) +# define AssertMsg(expr, msg) \ + do { \ + if (!(expr)) xf86ErrorF msg; \ + assert((expr)); \ + } while (0) +# define AssertPtr assert +# define AssertPtrReturn(pv, rcRet) do { assert(pv); if (pv) {} else return(rcRet); } while(0) +# define AssertRC(expr) assert (!expr) +#else +# define Assert(expr) do { } while(0) +# define AssertFailed() do { } while(0) +# define AssertMsg(expr, msg) do { } while(0) +# define AssertPtr(ptr) do { } while(0) +# define AssertPtrReturn(pv, rcRet) do { if (pv) {} else return(rcRet); } while(0) +# define AssertRC(expr) do { } while(0) +#endif + +#define DECLCALLBACK(type) type +#define DECLCALLBACKMEMBER(type, name) type (* name) +#if __GNUC__ >= 4 +# define DECLHIDDEN(type) __attribute__((visibility("hidden"))) type +#else +# define DECLHIDDEN(type) type +#endif +#define DECLINLINE(type) static __inline__ type + +#define _1K 1024 +#define ASMCompilerBarrier mem_barrier +#define RT_BIT(bit) ( 1U << (bit) ) +#define RT_BOOL(Value) ( !!(Value) ) +#define RT_BZERO(pv, cb) do { memset((pv), 0, cb); } while (0) +#define RT_CLAMP(Value, Min, Max) ( (Value) > (Max) ? (Max) : (Value) < (Min) ? (Min) : (Value) ) +#define RT_ELEMENTS(aArray) ( sizeof(aArray) / sizeof((aArray)[0]) ) +#define RTIOPORT unsigned short +#define RT_NOREF(...) (void)(__VA_ARGS__) +#define RT_OFFSETOF(type, member) offsetof(type, member) +#define RT_UOFFSETOF(type, member) offsetof(type, member) +#define RT_ZERO(Obj) RT_BZERO(&(Obj), sizeof(Obj)) +#define VALID_PTR(ptr) ( (uintptr_t)(ptr) + 0x1000U >= 0x2000U ) +#ifndef INT16_C +# define INT16_C(Value) (Value) +#endif +#ifndef UINT16_C +# define UINT16_C(Value) (Value) +#endif +#ifndef INT32_C +# define INT32_C(Value) (Value ## U) +#endif +#ifndef UINT32_C +# define UINT32_C(Value) (Value ## U) +#endif +#define RT_UNTRUSTED_GUEST +#define RT_UNTRUSTED_VOLATILE_GUEST volatile +#define RT_UNTRUSTED_HOST +#define RT_UNTRUSTED_VOLATILE_HOST volatile +#define RT_UNTRUSTED_HSTGST +#define RT_UNTRUSTED_VOLATILE_HSTGST volatile + +#define likely _X_LIKELY +#define unlikely _X_UNLIKELY + +/** + * A point in a two dimentional coordinate system. + */ +typedef struct RTPOINT +{ + /** X coordinate. */ + int32_t x; + /** Y coordinate. */ + int32_t y; +} RTPOINT; + +/** + * Rectangle data type, double point. + */ +typedef struct RTRECT +{ + /** left X coordinate. */ + int32_t xLeft; + /** top Y coordinate. */ + int32_t yTop; + /** right X coordinate. (exclusive) */ + int32_t xRight; + /** bottom Y coordinate. (exclusive) */ + int32_t yBottom; +} RTRECT; + +/** + * Rectangle data type, point + size. + */ +typedef struct RTRECT2 +{ + /** X coordinate. + * Unless stated otherwise, this is the top left corner. */ + int32_t x; + /** Y coordinate. + * Unless stated otherwise, this is the top left corner. */ + int32_t y; + /** The width. + * Unless stated otherwise, this is to the right of (x,y) and will not + * be a negative number. */ + int32_t cx; + /** The height. + * Unless stated otherwise, this is down from (x,y) and will not be a + * negative number. */ + int32_t cy; +} RTRECT2; + +/** + * The size of a rectangle. + */ +typedef struct RTRECTSIZE +{ + /** The width (along the x-axis). */ + uint32_t cx; + /** The height (along the y-axis). */ + uint32_t cy; +} RTRECTSIZE; + +/** @name Port I/O helpers + * @{ */ + +/** Write an 8-bit value to an I/O port. */ +#define VBVO_PORT_WRITE_U8(Port, Value) \ + outb(Port, Value) +/** Write a 16-bit value to an I/O port. */ +#define VBVO_PORT_WRITE_U16(Port, Value) \ + outw(Port, Value) +/** Write a 32-bit value to an I/O port. */ +#define VBVO_PORT_WRITE_U32(Port, Value) \ + outl(Port, Value) +/** Read an 8-bit value from an I/O port. */ +#define VBVO_PORT_READ_U8(Port) \ + inb(Port) +/** Read a 16-bit value from an I/O port. */ +#define VBVO_PORT_READ_U16(Port) \ + inw(Port) +/** Read a 32-bit value from an I/O port. */ +#define VBVO_PORT_READ_U32(Port) \ + inl(Port) + +/** @} */ + +#endif /* !VBOX_INCLUDED_Graphics_VBoxVideoIPRT_h */ diff --git a/src/VBox/Additions/x11/vboxvideo/edid.c b/src/VBox/Additions/x11/vboxvideo/edid.c new file mode 100644 index 00000000..54691cee --- /dev/null +++ b/src/VBox/Additions/x11/vboxvideo/edid.c @@ -0,0 +1,165 @@ +/* $Id: edid.c $ */ +/** @file + * + * Linux Additions X11 graphics driver, EDID construction + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * This file is based on drmmode_display.c from the X.Org xf86-video-intel + * driver with the following copyright notice: + * + * 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 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to 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. + * + * Authors: + * Dave Airlie <airlied@redhat.com> + * Michael Thayer <michael.thayer@oracle.com> + */ + +#include "misc.h" +#include "xf86DDC.h" +#include "xf86Crtc.h" +#include "vboxvideo.h" + +enum { EDID_SIZE = 128 }; + +const unsigned char g_acszEDIDBase[EDID_SIZE] = +{ + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, /* header */ + 0x58, 0x58, /* manufacturer (VBX) */ + 0x00, 0x00, /* product code */ + 0x00, 0x00,0x00, 0x00, /* serial number goes here */ + 0x01, /* week of manufacture */ + 0x00, /* year of manufacture */ + 0x01, 0x03, /* EDID version */ + 0x80, /* capabilities - digital */ + 0x00, /* horiz. res in cm, zero for projectors */ + 0x00, /* vert. res in cm */ + 0x78, /* display gamma (120 == 2.2). Should we ask the host for this? */ + 0xEE, /* features (standby, suspend, off, RGB, standard colour space, + * preferred timing mode) */ + 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54, + /* chromaticity for standard colour space - should we ask the host? */ + 0x00, 0x00, 0x00, /* no default timings */ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, /* no standard timings */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* descriptor block 1 goes here */ + 0x00, 0x00, 0x00, 0xFD, 0x00, /* descriptor block 2, monitor ranges */ + 0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, /* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */ + 0x00, 0x00, 0x00, 0xFC, 0x00, /* descriptor block 3, monitor name */ + 'V', 'B', 'O', 'X', ' ', 'm', 'o', 'n', 'i', 't', 'o', 'r', '\n', + 0x00, 0x00, 0x00, 0x10, 0x00, /* descriptor block 4: dummy data */ + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, + 0x00, /* number of extensions */ + 0x00 /* checksum goes here */ +}; + +static void fillDescBlockTimings(unsigned char *pchDescBlock, + DisplayModePtr mode) +{ + struct detailed_timings timing; + + timing.clock = mode->Clock * 1000; + timing.h_active = mode->HDisplay; + timing.h_blanking = mode->HTotal - mode->HDisplay; + timing.v_active = mode->VDisplay; + timing.v_blanking = mode->VTotal - mode->VDisplay; + timing.h_sync_off = mode->HSyncStart - mode->HDisplay; + timing.h_sync_width = mode->HSyncEnd - mode->HSyncStart; + timing.v_sync_off = mode->VSyncStart - mode->VDisplay; + timing.v_sync_width = mode->VSyncEnd - mode->VSyncStart; + pchDescBlock[0] = (timing.clock / 10000) & 0xff; + pchDescBlock[1] = (timing.clock / 10000) >> 8; + pchDescBlock[2] = timing.h_active & 0xff; + pchDescBlock[3] = timing.h_blanking & 0xff; + pchDescBlock[4] = (timing.h_active >> 4) & 0xf0; + pchDescBlock[4] |= (timing.h_blanking >> 8) & 0xf; + pchDescBlock[5] = timing.v_active & 0xff; + pchDescBlock[6] = timing.v_blanking & 0xff; + pchDescBlock[7] = (timing.v_active >> 4) & 0xf0; + pchDescBlock[7] |= (timing.v_blanking >> 8) & 0xf; + pchDescBlock[8] = timing.h_sync_off & 0xff; + pchDescBlock[9] = timing.h_sync_width & 0xff; + pchDescBlock[10] = (timing.v_sync_off << 4) & 0xf0; + pchDescBlock[10] |= timing.v_sync_width & 0xf; + pchDescBlock[11] = (timing.h_sync_off >> 2) & 0xC0; + pchDescBlock[11] |= (timing.h_sync_width >> 4) & 0x30; + pchDescBlock[11] |= (timing.v_sync_off >> 2) & 0xC; + pchDescBlock[11] |= (timing.v_sync_width >> 4) & 0x3; + pchDescBlock[12] = pchDescBlock[13] = pchDescBlock[14] + = pchDescBlock[15] = pchDescBlock[16] + = pchDescBlock[17] = 0; +} + + +static void setEDIDChecksum(unsigned char *pch) +{ + unsigned i, sum = 0; + for (i = 0; i < EDID_SIZE - 1; ++i) + sum += pch[i]; + pch[EDID_SIZE - 1] = (0x100 - (sum & 0xFF)) & 0xFF; +} + + +/** + * Construct an EDID for an output given a preferred mode. The main reason for + * doing this is to confound gnome-settings-deamon which tries to reset the + * last mode configuration if the same monitors are plugged in again, which is + * a reasonable thing to do but not what we want in a VM. We evily store + * the (empty) raw EDID data at the end of the structure so that it gets + * freed automatically along with the structure. + */ +Bool VBOXEDIDSet(xf86OutputPtr output, DisplayModePtr pmode) +{ + unsigned char *pch, *pchEDID; + xf86MonPtr pEDIDMon; + + pch = calloc(1, sizeof(xf86Monitor) + EDID_SIZE); + if (!pch) + { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "Can't allocate memory for EDID structure.\n"); + return FALSE; + } + pchEDID = pch + sizeof(xf86Monitor); + memcpy(pchEDID, g_acszEDIDBase, EDID_SIZE); + pchEDID[12] = pmode->HDisplay & 0xff; + pchEDID[13] = pmode->HDisplay >> 8; + pchEDID[14] = pmode->VDisplay & 0xff; + pchEDID[15] = pmode->VDisplay >> 8; + fillDescBlockTimings(pchEDID + 54, pmode); + setEDIDChecksum(pchEDID); + pEDIDMon = xf86InterpretEDID(output->scrn->scrnIndex, pchEDID); + if (!pEDIDMon) + { + free(pch); + return FALSE; + } + memcpy(pch, pEDIDMon, sizeof(xf86Monitor)); + free(pEDIDMon); + pEDIDMon = (xf86MonPtr)pch; + xf86OutputSetEDID(output, pEDIDMon); + return TRUE; +} diff --git a/src/VBox/Additions/x11/vboxvideo/getmode.c b/src/VBox/Additions/x11/vboxvideo/getmode.c new file mode 100644 index 00000000..396f730e --- /dev/null +++ b/src/VBox/Additions/x11/vboxvideo/getmode.c @@ -0,0 +1,325 @@ +/* $Id: getmode.c $ */ +/** @file + * VirtualBox X11 Additions graphics driver dynamic video mode functions. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "vboxvideo.h" + +#define NEED_XF86_TYPES +#include "xf86.h" + +#ifdef XORG_7X +# include <stdio.h> +# include <stdlib.h> +# include <string.h> +#endif + +#ifdef VBOXVIDEO_13 +# ifdef RT_OS_LINUX +# include <linux/input.h> +# ifndef EVIOCGRAB +# define EVIOCGRAB _IOW('E', 0x90, int) +# endif +# ifndef KEY_SWITCHVIDEOMODE +# define KEY_SWITCHVIDEOMODE 227 +# endif +# include <dirent.h> +# include <errno.h> +# include <fcntl.h> +# include <unistd.h> +# endif /* RT_OS_LINUX */ +#endif /* VBOXVIDEO_13 */ + +/************************************************************************** +* Main functions * +**************************************************************************/ + +/** + * Fills a display mode M with a built-in mode of name pszName and dimensions + * cx and cy. + */ +static void vboxFillDisplayMode(ScrnInfoPtr pScrn, DisplayModePtr m, + const char *pszName, unsigned cx, unsigned cy) +{ + VBOXPtr pVBox = pScrn->driverPrivate; + char szName[256]; + DisplayModePtr pPrev = m->prev; + DisplayModePtr pNext = m->next; + + if (!pszName) + { + sprintf(szName, "%ux%u", cx, cy); + pszName = szName; + } + TRACE_LOG("pszName=%s, cx=%u, cy=%u\n", pszName, cx, cy); + if (m->name) + free((void*)m->name); + memset(m, '\0', sizeof(*m)); + m->prev = pPrev; + m->next = pNext; + m->status = MODE_OK; + m->type = M_T_BUILTIN; + /* Older versions of VBox only support screen widths which are a multiple + * of 8 */ + if (pVBox->fAnyX) + m->HDisplay = cx; + else + m->HDisplay = cx & ~7; + m->HSyncStart = m->HDisplay + 2; + m->HSyncEnd = m->HDisplay + 4; + m->HTotal = m->HDisplay + 6; + m->VDisplay = cy; + m->VSyncStart = m->VDisplay + 2; + m->VSyncEnd = m->VDisplay + 4; + m->VTotal = m->VDisplay + 6; + m->Clock = m->HTotal * m->VTotal * 60 / 1000; /* kHz */ + m->name = xnfstrdup(pszName); +} + +/** + * Allocates an empty display mode and links it into the doubly linked list of + * modes pointed to by pScrn->modes. Returns a pointer to the newly allocated + * memory. + */ +static DisplayModePtr vboxAddEmptyScreenMode(ScrnInfoPtr pScrn) +{ + DisplayModePtr pMode = xnfcalloc(sizeof(DisplayModeRec), 1); + + TRACE_ENTRY(); + if (!pScrn->modes) + { + pScrn->modes = pMode; + pMode->next = pMode; + pMode->prev = pMode; + } + else + { + pMode->next = pScrn->modes; + pMode->prev = pScrn->modes->prev; + pMode->next->prev = pMode; + pMode->prev->next = pMode; + } + return pMode; +} + +/** + * Create display mode entries in the screen information structure for each + * of the graphics modes that we wish to support, that is: + * - A dynamic mode in first place which will be updated by the RandR code. + * - Any modes that the user requested in xorg.conf/XFree86Config. + */ +void vboxAddModes(ScrnInfoPtr pScrn) +{ + unsigned cx = 0; + unsigned cy = 0; + unsigned i; + DisplayModePtr pMode; + + /* Add two dynamic mode entries. When we receive a new size hint we will + * update whichever of these is not current. */ + pMode = vboxAddEmptyScreenMode(pScrn); + vboxFillDisplayMode(pScrn, pMode, NULL, 800, 600); + pMode = vboxAddEmptyScreenMode(pScrn); + vboxFillDisplayMode(pScrn, pMode, NULL, 800, 600); + /* Add any modes specified by the user. We assume here that the mode names + * reflect the mode sizes. */ + for (i = 0; pScrn->display->modes && pScrn->display->modes[i]; i++) + { + if (sscanf(pScrn->display->modes[i], "%ux%u", &cx, &cy) == 2) + { + pMode = vboxAddEmptyScreenMode(pScrn); + vboxFillDisplayMode(pScrn, pMode, pScrn->display->modes[i], cx, cy); + } + } +} + +/** Set the initial values for the guest screen size hints to standard values + * in case nothing else is available. */ +void VBoxInitialiseSizeHints(ScrnInfoPtr pScrn) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + unsigned i; + + for (i = 0; i < pVBox->cScreens; ++i) + { + pVBox->pScreens[i].aPreferredSize.cx = 800; + pVBox->pScreens[i].aPreferredSize.cy = 600; + pVBox->pScreens[i].afConnected = true; + } + /* Set up the first mode correctly to match the requested initial mode. */ + pScrn->modes->HDisplay = pVBox->pScreens[0].aPreferredSize.cx; + pScrn->modes->VDisplay = pVBox->pScreens[0].aPreferredSize.cy; +} + +static Bool useHardwareCursor(uint32_t fCursorCapabilities) +{ + if (fCursorCapabilities & VBOX_VBVA_CURSOR_CAPABILITY_HARDWARE) + return true; + return false; +} + +static void compareAndMaybeSetUseHardwareCursor(VBOXPtr pVBox, uint32_t fCursorCapabilities, Bool *pfChanged, Bool fSet) +{ + if (pVBox->fUseHardwareCursor != useHardwareCursor(fCursorCapabilities)) + *pfChanged = true; + if (fSet) + pVBox->fUseHardwareCursor = useHardwareCursor(fCursorCapabilities); +} + +#define COMPARE_AND_MAYBE_SET(pDest, src, pfChanged, fSet) \ +do { \ + if (*(pDest) != (src)) \ + { \ + if (fSet) \ + *(pDest) = (src); \ + *(pfChanged) = true; \ + } \ +} while(0) + +/** Read in information about the most recent size hints and cursor + * capabilities requested for the guest screens from HGSMI. */ +void vbvxReadSizesAndCursorIntegrationFromHGSMI(ScrnInfoPtr pScrn, Bool *pfNeedUpdate) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + int rc; + unsigned i; + Bool fChanged = false; + uint32_t fCursorCapabilities; + + if (!pVBox->fHaveHGSMIModeHints) + return; + rc = VBoxHGSMIGetModeHints(&pVBox->guestCtx, pVBox->cScreens, pVBox->paVBVAModeHints); + AssertMsg(rc == VINF_SUCCESS, ("VBoxHGSMIGetModeHints failed, rc=%d.\n", rc)); + for (i = 0; i < pVBox->cScreens; ++i) + if (pVBox->paVBVAModeHints[i].magic == VBVAMODEHINT_MAGIC) + { + COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].aPreferredSize.cx, pVBox->paVBVAModeHints[i].cx & 0x8fff, &fChanged, true); + COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].aPreferredSize.cy, pVBox->paVBVAModeHints[i].cy & 0x8fff, &fChanged, true); + COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].afConnected, RT_BOOL(pVBox->paVBVAModeHints[i].fEnabled), &fChanged, true); + COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].aPreferredLocation.x, (int32_t)pVBox->paVBVAModeHints[i].dx & 0x8fff, &fChanged, + true); + COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].aPreferredLocation.y, (int32_t)pVBox->paVBVAModeHints[i].dy & 0x8fff, &fChanged, + true); + if (pVBox->paVBVAModeHints[i].dx != ~(uint32_t)0 && pVBox->paVBVAModeHints[i].dy != ~(uint32_t)0) + COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].afHaveLocation, true, &fChanged, true); + else + COMPARE_AND_MAYBE_SET(&pVBox->pScreens[i].afHaveLocation, false, &fChanged, true); + } + rc = VBoxQueryConfHGSMI(&pVBox->guestCtx, VBOX_VBVA_CONF32_CURSOR_CAPABILITIES, &fCursorCapabilities); + AssertMsg(rc == VINF_SUCCESS, ("Getting VBOX_VBVA_CONF32_CURSOR_CAPABILITIES failed, rc=%d.\n", rc)); + compareAndMaybeSetUseHardwareCursor(pVBox, fCursorCapabilities, &fChanged, true); + if (pfNeedUpdate != NULL && fChanged) + *pfNeedUpdate = true; +} + +#undef COMPARE_AND_MAYBE_SET + +#ifdef VBOXVIDEO_13 +# ifdef RT_OS_LINUX +/** We have this for two purposes: one is to ensure that the X server is woken + * up when we get a video ACPI event. Two is to grab ACPI video events to + * prevent gnome-settings-daemon from seeing them, as older versions ignored + * the time stamp and handled them at the wrong time. */ +static void acpiEventHandler(int fd, void *pvData) +{ + struct input_event event; + ssize_t rc; + RT_NOREF(pvData); + + do + rc = read(fd, &event, sizeof(event)); + while (rc > 0 || (rc == -1 && errno == EINTR)); + /* Why do they return EAGAIN instead of zero bytes read like everyone else does? */ + AssertMsg(rc != -1 || errno == EAGAIN, ("Reading ACPI input event failed.\n")); +} + +void vbvxSetUpLinuxACPI(ScreenPtr pScreen) +{ + static const char s_szDevInput[] = "/dev/input/"; + struct dirent *pDirent; + char szFile[sizeof(s_szDevInput) + sizeof(pDirent->d_name) + 16]; + VBOXPtr pVBox = VBOXGetRec(xf86Screens[pScreen->myNum]); + DIR *pDir; + int fd = -1; + + if (pVBox->fdACPIDevices != -1 || pVBox->hACPIEventHandler != NULL) + FatalError("ACPI input file descriptor not initialised correctly.\n"); + pDir = opendir("/dev/input"); + if (pDir == NULL) + return; + memcpy(szFile, s_szDevInput, sizeof(s_szDevInput)); + for (pDirent = readdir(pDir); pDirent != NULL; pDirent = readdir(pDir)) + { + if (strncmp(pDirent->d_name, "event", sizeof("event") - 1) == 0) + { +#define BITS_PER_BLOCK (sizeof(unsigned long) * 8) + char szDevice[64] = ""; + unsigned long afKeys[KEY_MAX / BITS_PER_BLOCK]; + size_t const cchName = strlen(pDirent->d_name); + if (cchName + sizeof(s_szDevInput) > sizeof(szFile)) + continue; + memcpy(&szFile[sizeof(s_szDevInput) - 1], pDirent->d_name, cchName + 1); + if (fd != -1) + close(fd); + fd = open(szFile, O_RDONLY | O_NONBLOCK); + if ( fd == -1 + || ioctl(fd, EVIOCGNAME(sizeof(szDevice)), szDevice) == -1 + || strcmp(szDevice, "Video Bus") != 0) + continue; + if ( ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(afKeys)), afKeys) == -1 + || (( afKeys[KEY_SWITCHVIDEOMODE / BITS_PER_BLOCK] + >> KEY_SWITCHVIDEOMODE % BITS_PER_BLOCK) & 1) == 0) + break; + if (ioctl(fd, EVIOCGRAB, (void *)1) != 0) + break; + pVBox->hACPIEventHandler + = xf86AddGeneralHandler(fd, acpiEventHandler, pScreen); + if (pVBox->hACPIEventHandler == NULL) + break; + pVBox->fdACPIDevices = fd; + fd = -1; + break; +#undef BITS_PER_BLOCK + } + } + if (fd != -1) + close(fd); + closedir(pDir); +} + +void vbvxCleanUpLinuxACPI(ScreenPtr pScreen) +{ + VBOXPtr pVBox = VBOXGetRec(xf86Screens[pScreen->myNum]); + if (pVBox->fdACPIDevices != -1) + close(pVBox->fdACPIDevices); + pVBox->fdACPIDevices = -1; + xf86RemoveGeneralHandler(pVBox->hACPIEventHandler); + pVBox->hACPIEventHandler = NULL; +} +# endif /* RT_OS_LINUX */ +#endif /* VBOXVIDEO_13 */ diff --git a/src/VBox/Additions/x11/vboxvideo/hgsmimemalloc.c b/src/VBox/Additions/x11/vboxvideo/hgsmimemalloc.c new file mode 100644 index 00000000..925b332f --- /dev/null +++ b/src/VBox/Additions/x11/vboxvideo/hgsmimemalloc.c @@ -0,0 +1,104 @@ +/* $Id: hgsmimemalloc.c $ */ +/* + * Copyright (C) 2017-2019 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Memory allocator + * ---------------- + * + * Implementation + * -------------- + * + * Since the X.Org driver is single threaded and works using an allocate, + * submit and free pattern, we replace the generic allocator with a simple + * Boolean. Need more be said? + * + * bird> Yes, it's buggy. You never set fAllocated. See HGSMIMAAlloc(). + */ + +#include <VBoxVideoIPRT.h> +#include <HGSMIMemAlloc.h> +#include <HGSMI.h> + +int HGSMIMAInit(HGSMIMADATA *pMA, const HGSMIAREA *pArea, + HGSMIOFFSET *paDescriptors, uint32_t cDescriptors, HGSMISIZE cbMaxBlock, + const HGSMIENV *pEnv) +{ + (void)paDescriptors; + (void)cDescriptors; + (void)cbMaxBlock; + (void)pEnv; + if (!(pArea->cbArea < UINT32_C(0x80000000))) + return VERR_INVALID_PARAMETER; + if (!(pArea->cbArea >= HGSMI_MA_BLOCK_SIZE_MIN)) + return VERR_INVALID_PARAMETER; + + pMA->area = *pArea; + pMA->fAllocated = false; + return VINF_SUCCESS; +} + +void HGSMIMAUninit(HGSMIMADATA *pMA) +{ + (void)pMA; +} + +static HGSMIOFFSET HGSMIMAPointerToOffset(const HGSMIMADATA *pMA, const void RT_UNTRUSTED_VOLATILE_GUEST *pv) +{ + if (HGSMIAreaContainsPointer(&pMA->area, pv)) + return HGSMIPointerToOffset(&pMA->area, pv); + + AssertFailed(); + return HGSMIOFFSET_VOID; +} + +static void RT_UNTRUSTED_VOLATILE_GUEST *HGSMIMAOffsetToPointer(const HGSMIMADATA *pMA, HGSMIOFFSET off) +{ + if (HGSMIAreaContainsOffset(&pMA->area, off)) + return HGSMIOffsetToPointer(&pMA->area, off); + + AssertFailed(); + return NULL; +} + +void RT_UNTRUSTED_VOLATILE_GUEST *HGSMIMAAlloc(HGSMIMADATA *pMA, HGSMISIZE cb) +{ + (void)cb; + if (pMA->fAllocated) + return NULL; + HGSMIOFFSET off = pMA->area.offBase; + return HGSMIMAOffsetToPointer(pMA, off); + pMA->fAllocated = true; /** @todo r=bird: Errr. what's this doing *after* the return statement? */ +} + +void HGSMIMAFree(HGSMIMADATA *pMA, void RT_UNTRUSTED_VOLATILE_GUEST *pv) +{ + HGSMIOFFSET off = HGSMIMAPointerToOffset(pMA, pv); + if (off != HGSMIOFFSET_VOID) + pMA->fAllocated = false; + else + AssertFailed(); +} + diff --git a/src/VBox/Additions/x11/vboxvideo/pointer.c b/src/VBox/Additions/x11/vboxvideo/pointer.c new file mode 100644 index 00000000..cd8c87ff --- /dev/null +++ b/src/VBox/Additions/x11/vboxvideo/pointer.c @@ -0,0 +1,496 @@ +/* $Id: pointer.c $ */ +/** @file + * VirtualBox X11 Additions graphics driver utility functions + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION 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 PCIACCESS +# include "xf86Pci.h" +# include <Pci.h> +#endif + +#include "xf86.h" +#define NEED_XF86_TYPES +#include "compiler.h" +#include "cursorstr.h" +#include "servermd.h" + +#include "vboxvideo.h" + +#ifdef XORG_7X +# include <stdlib.h> +# include <string.h> +#endif + +#define VBOX_MAX_CURSOR_WIDTH 64 +#define VBOX_MAX_CURSOR_HEIGHT 64 + +/************************************************************************** +* Debugging functions and macros * +**************************************************************************/ + +/* #define DEBUG_POINTER */ + +#ifdef DEBUG +# define PUT_PIXEL(c) ErrorF ("%c", c) +#else /* DEBUG_VIDEO not defined */ +# define PUT_PIXEL(c) do { } while(0) +#endif /* DEBUG_VIDEO not defined */ + +/** Macro to printf an error message and return from a function */ +#define RETERROR(scrnIndex, RetVal, ...) \ + do \ + { \ + xf86DrvMsg(scrnIndex, X_ERROR, __VA_ARGS__); \ + return RetVal; \ + } \ + while (0) + +/** Structure to pass cursor image data between realise_cursor() and + * load_cursor_image(). The members match the parameters to + * @a VBoxHGSMIUpdatePointerShape(). */ +struct vboxCursorImage +{ + uint32_t fFlags; + uint32_t cHotX; + uint32_t cHotY; + uint32_t cWidth; + uint32_t cHeight; + uint8_t *pPixels; + uint32_t cbLength; +}; + +#ifdef DEBUG_POINTER +static void +vbox_show_shape(unsigned short w, unsigned short h, CARD32 bg, unsigned char *image) +{ + size_t x, y; + unsigned short pitch; + CARD32 *color; + unsigned char *mask; + size_t sizeMask; + + image += sizeof(struct vboxCursorImage); + mask = image; + pitch = (w + 7) / 8; + sizeMask = (pitch * h + 3) & ~3; + color = (CARD32 *)(image + sizeMask); + + TRACE_ENTRY(); + for (y = 0; y < h; ++y, mask += pitch, color += w) + { + for (x = 0; x < w; ++x) + { + if (mask[x / 8] & (1 << (7 - (x % 8)))) + ErrorF (" "); + else + { + CARD32 c = color[x]; + if (c == bg) + ErrorF("Y"); + else + ErrorF("X"); + } + } + ErrorF("\n"); + } +} +#endif + +/************************************************************************** +* Main functions * +**************************************************************************/ + +void vbvxCursorTerm(VBOXPtr pVBox) +{ + TRACE_ENTRY(); + + xf86DestroyCursorInfoRec(pVBox->pCurs); + pVBox->pCurs = NULL; + TRACE_EXIT(); +} + +static void +vbox_vmm_hide_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox) +{ + int rc; + RT_NOREF(pScrn); + + rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, 0, 0, 0, 0, 0, NULL, 0); + AssertMsg(rc == VINF_SUCCESS, ("Could not hide the virtual mouse pointer, VBox error %d.\n", rc)); +} + +static void +vbox_vmm_show_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox) +{ + int rc; + RT_NOREF(pScrn); + + if (!pVBox->fUseHardwareCursor) + return; + rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, VBOX_MOUSE_POINTER_VISIBLE, + 0, 0, 0, 0, NULL, 0); + AssertMsg(rc == VINF_SUCCESS, ("Could not unhide the virtual mouse pointer.\n")); +} + +static void +vbox_vmm_load_cursor_image(ScrnInfoPtr pScrn, VBOXPtr pVBox, + unsigned char *pvImage) +{ + int rc; + struct vboxCursorImage *pImage; + pImage = (struct vboxCursorImage *)pvImage; + RT_NOREF(pScrn); + +#ifdef DEBUG_POINTER + vbox_show_shape(pImage->cWidth, pImage->cHeight, 0, pvImage); +#endif + + rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, pImage->fFlags, + pImage->cHotX, pImage->cHotY, pImage->cWidth, pImage->cHeight, + pImage->pPixels, pImage->cbLength); + AssertMsg(rc == VINF_SUCCESS, ("Unable to set the virtual mouse pointer image.\n")); +} + +static void +vbox_set_cursor_colors(ScrnInfoPtr pScrn, int bg, int fg) +{ + RT_NOREF(pScrn); + RT_NOREF(bg); + RT_NOREF(fg); + /* ErrorF("vbox_set_cursor_colors NOT IMPLEMENTED\n"); */ +} + + +static void +vbox_set_cursor_position(ScrnInfoPtr pScrn, int x, int y) +{ + VBOXPtr pVBox = pScrn->driverPrivate; + + /* This currently does nothing. */ + VBoxHGSMICursorPosition(&pVBox->guestCtx, true, x, y, NULL, NULL); +} + +static void +vbox_hide_cursor(ScrnInfoPtr pScrn) +{ + VBOXPtr pVBox = pScrn->driverPrivate; + + vbox_vmm_hide_cursor(pScrn, pVBox); +} + +static void +vbox_show_cursor(ScrnInfoPtr pScrn) +{ + VBOXPtr pVBox = pScrn->driverPrivate; + + vbox_vmm_show_cursor(pScrn, pVBox); +} + +static void +vbox_load_cursor_image(ScrnInfoPtr pScrn, unsigned char *image) +{ + VBOXPtr pVBox = pScrn->driverPrivate; + + vbox_vmm_load_cursor_image(pScrn, pVBox, image); +} + +static Bool +vbox_use_hw_cursor(ScreenPtr pScreen, CursorPtr pCurs) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + VBOXPtr pVBox = pScrn->driverPrivate; + RT_NOREF(pCurs); + return pVBox->fUseHardwareCursor; +} + +static unsigned char +color_to_byte(unsigned c) +{ + return (c >> 8) & 0xff; +} + +static unsigned char * +vbox_realize_cursor(xf86CursorInfoPtr infoPtr, CursorPtr pCurs) +{ + VBOXPtr pVBox; + CursorBitsPtr bitsp; + unsigned short w, h, x, y; + unsigned char *c, *p, *pm, *ps, *m; + size_t sizeRequest, sizeRgba, sizeMask, srcPitch, dstPitch; + CARD32 fc, bc, *cp; + int scrnIndex = infoPtr->pScrn->scrnIndex; + struct vboxCursorImage *pImage; + + pVBox = infoPtr->pScrn->driverPrivate; + bitsp = pCurs->bits; + w = bitsp->width; + h = bitsp->height; + + if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT) + RETERROR(scrnIndex, NULL, + "Error invalid cursor dimensions %dx%d\n", w, h); + + if ((bitsp->xhot > w) || (bitsp->yhot > h)) + RETERROR(scrnIndex, NULL, + "Error invalid cursor hotspot location %dx%d (max %dx%d)\n", + bitsp->xhot, bitsp->yhot, w, h); + + srcPitch = PixmapBytePad (bitsp->width, 1); + dstPitch = (w + 7) / 8; + sizeMask = ((dstPitch * h) + 3) & (size_t) ~3; + sizeRgba = w * h * 4; + sizeRequest = sizeMask + sizeRgba + sizeof(*pImage); + + p = c = calloc (1, sizeRequest); + if (!c) + RETERROR(scrnIndex, NULL, + "Error failed to alloc %lu bytes for cursor\n", + (unsigned long) sizeRequest); + + pImage = (struct vboxCursorImage *)p; + pImage->pPixels = m = p + sizeof(*pImage); + cp = (CARD32 *)(m + sizeMask); + + TRACE_LOG ("w=%d h=%d sm=%d sr=%d p=%d\n", + w, h, (int) sizeMask, (int) sizeRgba, (int) dstPitch); + TRACE_LOG ("m=%p c=%p cp=%p\n", m, c, (void *)cp); + + fc = color_to_byte (pCurs->foreBlue) + | (color_to_byte (pCurs->foreGreen) << 8) + | (color_to_byte (pCurs->foreRed) << 16); + + bc = color_to_byte (pCurs->backBlue) + | (color_to_byte (pCurs->backGreen) << 8) + | (color_to_byte (pCurs->backRed) << 16); + + /* + * Convert the Xorg source/mask bits to the and/xor bits VBox needs. + * Xorg: + * The mask is a bitmap indicating which parts of the cursor are + * transparent and which parts are drawn. The source is a bitmap + * indicating which parts of the non-transparent portion of the + * the cursor should be painted in the foreground color and which + * should be painted in the background color. By default, set bits + * indicate the opaque part of the mask bitmap and clear bits + * indicate the transparent part. + * VBox: + * The color data is the XOR mask. The AND mask bits determine + * which pixels of the color data (XOR mask) will replace (overwrite) + * the screen pixels (AND mask bit = 0) and which ones will be XORed + * with existing screen pixels (AND mask bit = 1). + * For example when you have the AND mask all 0, then you see the + * correct mouse pointer image surrounded by black square. + */ + for (pm = bitsp->mask, ps = bitsp->source, y = 0; + y < h; + ++y, pm += srcPitch, ps += srcPitch, m += dstPitch) + { + for (x = 0; x < w; ++x) + { + if (pm[x / 8] & (1 << (x % 8))) + { + /* opaque, leave AND mask bit at 0 */ + if (ps[x / 8] & (1 << (x % 8))) + { + *cp++ = fc; + PUT_PIXEL('X'); + } + else + { + *cp++ = bc; + PUT_PIXEL('*'); + } + } + else + { + /* transparent, set AND mask bit */ + m[x / 8] |= 1 << (7 - (x % 8)); + /* don't change the screen pixel */ + *cp++ = 0; + PUT_PIXEL(' '); + } + } + PUT_PIXEL('\n'); + } + + pImage->cWidth = w; + pImage->cHeight = h; + pImage->cHotX = bitsp->xhot; + pImage->cHotY = bitsp->yhot; + pImage->fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE; + pImage->cbLength = sizeRequest - sizeof(*pImage); + +#ifdef DEBUG_POINTER + ErrorF("shape = %p\n", p); + vbox_show_shape(w, h, bc, c); +#endif + + return p; +} + +#ifdef ARGB_CURSOR +static Bool +vbox_use_hw_cursor_argb(ScreenPtr pScreen, CursorPtr pCurs) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + VBOXPtr pVBox = pScrn->driverPrivate; + + if (!pVBox->fUseHardwareCursor) + return FALSE; + if ( (pCurs->bits->height > VBOX_MAX_CURSOR_HEIGHT) + || (pCurs->bits->width > VBOX_MAX_CURSOR_WIDTH) + || (pScrn->bitsPerPixel <= 8)) + return FALSE; + return TRUE; +} + + +static void +vbox_load_cursor_argb(ScrnInfoPtr pScrn, CursorPtr pCurs) +{ + VBOXPtr pVBox; + CursorBitsPtr bitsp; + unsigned short w, h; + unsigned short cx, cy; + unsigned char *pm; + CARD32 *pc; + size_t sizeData, sizeMask; + CARD8 *p; + int scrnIndex; + uint32_t fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE + | VBOX_MOUSE_POINTER_ALPHA; + + pVBox = pScrn->driverPrivate; + bitsp = pCurs->bits; + w = bitsp->width; + h = bitsp->height; + scrnIndex = pScrn->scrnIndex; + + /* Mask must be generated for alpha cursors, that is required by VBox. */ + /* note: (michael) the next struct must be 32bit aligned. */ + sizeMask = ((w + 7) / 8 * h + 3) & ~3; + + if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT) + RETERROR(scrnIndex, , + "Error invalid cursor dimensions %dx%d\n", w, h); + + if ((bitsp->xhot > w) || (bitsp->yhot > h)) + RETERROR(scrnIndex, , + "Error invalid cursor hotspot location %dx%d (max %dx%d)\n", + bitsp->xhot, bitsp->yhot, w, h); + + sizeData = w * h * 4 + sizeMask; + p = calloc(1, sizeData); + if (!p) + RETERROR(scrnIndex, , + "Error failed to alloc %lu bytes for cursor\n", + (unsigned long)sizeData); + + memcpy(p + sizeMask, bitsp->argb, w * h * 4); + + /* Emulate the AND mask. */ + pm = p; + pc = bitsp->argb; + + /* Init AND mask to 1 */ + memset(pm, 0xFF, sizeMask); + + /* + * The additions driver must provide the AND mask for alpha cursors. The host frontend + * which can handle alpha channel, will ignore the AND mask and draw an alpha cursor. + * But if the host does not support ARGB, then it simply uses the AND mask and the color + * data to draw a normal color cursor. + */ + for (cy = 0; cy < h; cy++) + { + unsigned char bitmask = 0x80; + + for (cx = 0; cx < w; cx++, bitmask >>= 1) + { + if (bitmask == 0) + bitmask = 0x80; + + if (pc[cx] >= 0xF0000000) + pm[cx / 8] &= ~bitmask; + } + + /* Point to next source and dest scans */ + pc += w; + pm += (w + 7) / 8; + } + + VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, fFlags, bitsp->xhot, + bitsp->yhot, w, h, p, sizeData); + free(p); +} +#endif + +Bool vbvxCursorInit(ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + VBOXPtr pVBox = pScrn->driverPrivate; + xf86CursorInfoPtr pCurs = NULL; + Bool rc = TRUE; + + TRACE_ENTRY(); + pVBox->pCurs = pCurs = xf86CreateCursorInfoRec(); + if (!pCurs) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to create X Window cursor information structures for virtual mouse.\n"); + rc = FALSE; + } + if (rc) { + pCurs->MaxWidth = VBOX_MAX_CURSOR_WIDTH; + pCurs->MaxHeight = VBOX_MAX_CURSOR_HEIGHT; + pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP + | HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1 + | HARDWARE_CURSOR_BIT_ORDER_MSBFIRST + | HARDWARE_CURSOR_UPDATE_UNHIDDEN; + + pCurs->SetCursorColors = vbox_set_cursor_colors; + pCurs->SetCursorPosition = vbox_set_cursor_position; + pCurs->LoadCursorImage = vbox_load_cursor_image; + pCurs->HideCursor = vbox_hide_cursor; + pCurs->ShowCursor = vbox_show_cursor; + pCurs->UseHWCursor = vbox_use_hw_cursor; + pCurs->RealizeCursor = vbox_realize_cursor; + +#ifdef ARGB_CURSOR + pCurs->UseHWCursorARGB = vbox_use_hw_cursor_argb; + pCurs->LoadCursorARGB = vbox_load_cursor_argb; +#endif + + rc = xf86InitCursor(pScreen, pCurs); + } + if (!rc) + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to enable mouse pointer integration.\n"); + if (!rc && (pCurs != NULL)) + xf86DestroyCursorInfoRec(pCurs); + return rc; +} diff --git a/src/VBox/Additions/x11/vboxvideo/setmode.c b/src/VBox/Additions/x11/vboxvideo/setmode.c new file mode 100644 index 00000000..609af138 --- /dev/null +++ b/src/VBox/Additions/x11/vboxvideo/setmode.c @@ -0,0 +1,129 @@ +/* $Id: setmode.c $ */ +/** @file + * Linux Additions X11 graphics driver, mode setting + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * This file is based on X11 VESA driver (hardly any traces left here): + * + * Copyright (c) 2000 by Conectiva S.A. (http://www.conectiva.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS 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 COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 Conectiva Linux shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization from + * Conectiva Linux. + * + * Authors: Paulo César Pereira de Andrade <pcpa@conectiva.com.br> + * Michael Thayer <michael.thayer@oracle.com> + */ + +#ifdef XORG_7X +/* We include <unistd.h> for Solaris below, and the ANSI C emulation layer + * interferes with that. */ +# define _XF86_ANSIC_H +# define XF86_LIBC_H +# include <string.h> +#endif +#include "vboxvideo.h" +#include "xf86.h" + +/* VGA hardware functions for setting and restoring text mode */ +#include "vgaHW.h" + +#ifdef RT_OS_SOLARIS +# include <sys/vuid_event.h> +# include <sys/msio.h> +# include <errno.h> +# include <fcntl.h> +# include <unistd.h> +#endif + +/** Clear the virtual framebuffer in VRAM. Optionally also clear up to the + * size of a new framebuffer. Framebuffer sizes larger than available VRAM + * be treated as zero and passed over. */ +void vbvxClearVRAM(ScrnInfoPtr pScrn, size_t cbOldSize, size_t cbNewSize) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + + /* Assume 32BPP - this is just a sanity test. */ + AssertMsg( cbOldSize / 4 <= VBOX_VIDEO_MAX_VIRTUAL * VBOX_VIDEO_MAX_VIRTUAL + && cbNewSize / 4 <= VBOX_VIDEO_MAX_VIRTUAL * VBOX_VIDEO_MAX_VIRTUAL, + ("cbOldSize=%llu cbNewSize=%llu, max=%u.\n", (unsigned long long)cbOldSize, (unsigned long long)cbNewSize, + VBOX_VIDEO_MAX_VIRTUAL * VBOX_VIDEO_MAX_VIRTUAL)); + if (cbOldSize > (size_t)pVBox->cbFBMax) + cbOldSize = pVBox->cbFBMax; + if (cbNewSize > (size_t)pVBox->cbFBMax) + cbNewSize = pVBox->cbFBMax; + memset(pVBox->base, 0, max(cbOldSize, cbNewSize)); +} + +/** Set a graphics mode. Poke any required values into registers, do an HGSMI + * mode set and tell the host we support advanced graphics functions. + */ +void vbvxSetMode(ScrnInfoPtr pScrn, unsigned cDisplay, unsigned cWidth, unsigned cHeight, int x, int y, Bool fEnabled, + Bool fConnected, struct vbvxFrameBuffer *pFrameBuffer) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + uint32_t offStart; + uint16_t fFlags; + int rc; + Bool fEnabledAndVisible = fEnabled && x + cWidth <= pFrameBuffer->cWidth && y + cHeight <= pFrameBuffer->cHeight; + /* Recent host code has a flag to blank the screen; older code needs BPP set to zero. */ + uint32_t cBPP = fEnabledAndVisible || pVBox->fHostHasScreenBlankingFlag ? pFrameBuffer->cBPP : 0; + + TRACE_LOG("cDisplay=%u, cWidth=%u, cHeight=%u, x=%d, y=%d, fEnabled=%d, fConnected=%d, pFrameBuffer: { x0=%d, y0=%d, cWidth=%u, cHeight=%u, cBPP=%u }\n", + cDisplay, cWidth, cHeight, x, y, fEnabled, fConnected, pFrameBuffer->x0, pFrameBuffer->y0, pFrameBuffer->cWidth, + pFrameBuffer->cHeight, pFrameBuffer->cBPP); + AssertMsg(cWidth != 0 && cHeight != 0, ("cWidth = 0 or cHeight = 0\n")); + offStart = (y * pFrameBuffer->cWidth + x) * pFrameBuffer->cBPP / 8; + if (cDisplay == 0 && fEnabled) + VBoxVideoSetModeRegisters(cWidth, cHeight, pFrameBuffer->cWidth, pFrameBuffer->cBPP, 0, x, y); + fFlags = VBVA_SCREEN_F_ACTIVE; + fFlags |= (fConnected ? 0 : VBVA_SCREEN_F_DISABLED); + fFlags |= (!fEnabledAndVisible && pVBox->fHostHasScreenBlankingFlag ? VBVA_SCREEN_F_BLANK : 0); + VBoxHGSMIProcessDisplayInfo(&pVBox->guestCtx, cDisplay, x - pFrameBuffer->x0, y - pFrameBuffer->y0, offStart, + pFrameBuffer->cWidth * pFrameBuffer->cBPP / 8, cWidth, cHeight, cBPP, fFlags); + rc = VBoxHGSMIUpdateInputMapping(&pVBox->guestCtx, 0 - pFrameBuffer->x0, 0 - pFrameBuffer->y0, pFrameBuffer->cWidth, + pFrameBuffer->cHeight); + if (RT_FAILURE(rc)) + FatalError("Failed to update the input mapping.\n"); +} + +/** Tell the virtual mouse device about the new virtual desktop size. */ +void vbvxSetSolarisMouseRange(int width, int height) +{ +#ifdef RT_OS_SOLARIS + int rc; + int hMouse = open("/dev/mouse", O_RDWR); + + if (hMouse >= 0) + { + do { + Ms_screen_resolution Res = { height, width }; + rc = ioctl(hMouse, MSIOSRESOLUTION, &Res); + } while ((rc != 0) && (errno == EINTR)); + close(hMouse); + } +#else + (void)width; (void)height; +#endif +} diff --git a/src/VBox/Additions/x11/vboxvideo/vboxvideo.c b/src/VBox/Additions/x11/vboxvideo/vboxvideo.c new file mode 100644 index 00000000..00ba12af --- /dev/null +++ b/src/VBox/Additions/x11/vboxvideo/vboxvideo.c @@ -0,0 +1,1491 @@ +/* $Id: vboxvideo.c $ */ +/** @file + * Linux Additions X11 graphics driver + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * This file is based on the X.Org VESA driver: + * + * Copyright (c) 2000 by Conectiva S.A. (http://www.conectiva.com) + * Copyright 2008 Red Hat, Inc. + * Copyright 2012 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 Conectiva Linux shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization from + * Conectiva Linux. + * + * Authors: Paulo César Pereira de Andrade <pcpa@conectiva.com.br> + * David Dawes <dawes@xfree86.org> + * Adam Jackson <ajax@redhat.com> + * Dave Airlie <airlied@redhat.com> + * Michael Thayer <michael.thayer@oracle.com> + */ + +#include "vboxvideo.h" +#include <VBoxVideoVBE.h> + +/* Basic definitions and functions needed by all drivers. */ +#include "xf86.h" +/* For video memory mapping. */ +#include "xf86_OSproc.h" +#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 6 +/* PCI resources. */ +# include "xf86Resources.h" +#endif +/* Generic server linear frame-buffer APIs. */ +#include "fb.h" +/* Colormap and visual handling. */ +#include "micmap.h" +#include "xf86cmap.h" +/* ShadowFB support */ +#include "shadowfb.h" +/* VGA hardware functions for setting and restoring text mode */ +#include "vgaHW.h" +#ifdef VBOXVIDEO_13 +/* X.org 1.3+ mode setting */ +# define _HAVE_STRING_ARCH_strsep /* bits/string2.h, __strsep_1c. */ +# include "xf86Crtc.h" +# include "xf86Modes.h" +/* For xf86RandR12GetOriginalVirtualSize(). */ +# include "xf86RandR12.h" +#endif +/* For setting the root window property. */ +#include "property.h" +#include <X11/Xatom.h> + +#ifdef XORG_7X +# include <stdlib.h> +# include <string.h> +# include <fcntl.h> +# include <unistd.h> +#endif + +/* Mandatory functions */ + +static const OptionInfoRec * VBOXAvailableOptions(int chipid, int busid); +static void VBOXIdentify(int flags); +#ifndef PCIACCESS +static Bool VBOXProbe(DriverPtr drv, int flags); +#else +static Bool VBOXPciProbe(DriverPtr drv, int entity_num, + struct pci_device *dev, intptr_t match_data); +#endif +static Bool VBOXPreInit(ScrnInfoPtr pScrn, int flags); +static Bool VBOXScreenInit(ScreenPtr pScreen, int argc, char **argv); +static Bool VBOXEnterVT(ScrnInfoPtr pScrn); +static void VBOXLeaveVT(ScrnInfoPtr pScrn); +static Bool VBOXCloseScreen(ScreenPtr pScreen); +#ifndef VBOXVIDEO_13 +static Bool VBOXSaveScreen(ScreenPtr pScreen, int mode); +#endif +static Bool VBOXSwitchMode(ScrnInfoPtr pScrn, DisplayModePtr pMode); +static void VBOXAdjustFrame(ScrnInfoPtr pScrn, int x, int y); +static void VBOXFreeScreen(ScrnInfoPtr pScrn); +#ifndef VBOXVIDEO_13 +static void VBOXDisplayPowerManagementSet(ScrnInfoPtr pScrn, int mode, int flags); +#endif + +/* locally used functions */ +static Bool VBOXMapVidMem(ScrnInfoPtr pScrn); +static void VBOXUnmapVidMem(ScrnInfoPtr pScrn); +static void VBOXSaveMode(ScrnInfoPtr pScrn); +static void VBOXRestoreMode(ScrnInfoPtr pScrn); +static void setSizesAndCursorIntegration(ScrnInfoPtr pScrn, Bool fScreenInitTime); + +#ifndef XF86_SCRN_INTERFACE +# define xf86ScreenToScrn(pScreen) xf86Screens[(pScreen)->myNum] +# define xf86ScrnToScreen(pScrn) screenInfo.screens[(pScrn)->scrnIndex] +#endif + +static inline void VBOXSetRec(ScrnInfoPtr pScrn) +{ + if (!pScrn->driverPrivate) + { + VBOXPtr pVBox = (VBOXPtr)xnfcalloc(sizeof(VBOXRec), 1); + pScrn->driverPrivate = pVBox; +#if defined(VBOXVIDEO_13) && defined(RT_OS_LINUX) + pVBox->fdACPIDevices = -1; +#endif + } +} + +enum GenericTypes +{ + CHIP_VBOX_GENERIC +}; + +#ifdef PCIACCESS +static const struct pci_id_match vbox_device_match[] = { + { + VBOX_VENDORID, VBOX_DEVICEID, PCI_MATCH_ANY, PCI_MATCH_ANY, + 0, 0, 0 + }, + + { 0, 0, 0 }, +}; +#endif + +/* Supported chipsets */ +static SymTabRec VBOXChipsets[] = +{ + {VBOX_DEVICEID, "vbox"}, + {-1, NULL} +}; + +static PciChipsets VBOXPCIchipsets[] = { + { VBOX_DEVICEID, VBOX_DEVICEID, RES_SHARED_VGA }, + { -1, -1, RES_UNDEFINED }, +}; + +/* + * This contains the functions needed by the server after loading the + * driver module. It must be supplied, and gets added the driver list by + * the Module Setup function in the dynamic case. In the static case a + * reference to this is compiled in, and this requires that the name of + * this DriverRec be an upper-case version of the driver name. + */ + +#ifdef XORG_7X +_X_EXPORT +#endif +DriverRec VBOXVIDEO = { + VBOX_VERSION, + VBOX_DRIVER_NAME, + VBOXIdentify, +#ifdef PCIACCESS + NULL, +#else + VBOXProbe, +#endif + VBOXAvailableOptions, + NULL, + 0, +#ifdef XORG_7X + NULL, +#endif +#ifdef PCIACCESS + vbox_device_match, + VBOXPciProbe +#endif +}; + +/* No options for now */ +static const OptionInfoRec VBOXOptions[] = { + { -1, NULL, OPTV_NONE, {0}, FALSE } +}; + +#ifndef XORG_7X +/* + * List of symbols from other modules that this module references. This + * list is used to tell the loader that it is OK for symbols here to be + * unresolved providing that it hasn't been told that they haven't been + * told that they are essential via a call to xf86LoaderReqSymbols() or + * xf86LoaderReqSymLists(). The purpose is this is to avoid warnings about + * unresolved symbols that are not required. + */ +static const char *fbSymbols[] = { + "fbPictureInit", + "fbScreenInit", + NULL +}; + +static const char *shadowfbSymbols[] = { + "ShadowFBInit2", + NULL +}; + +static const char *ramdacSymbols[] = { + "xf86DestroyCursorInfoRec", + "xf86InitCursor", + "xf86CreateCursorInfoRec", + NULL +}; + +static const char *vgahwSymbols[] = { + "vgaHWFreeHWRec", + "vgaHWGetHWRec", + "vgaHWGetIOBase", + "vgaHWGetIndex", + "vgaHWRestore", + "vgaHWSave", + "vgaHWSetStdFuncs", + NULL +}; +#endif /* !XORG_7X */ + +/** Resize the virtual framebuffer. */ +static Bool adjustScreenPixmap(ScrnInfoPtr pScrn, int width, int height) +{ + ScreenPtr pScreen = xf86ScrnToScreen(pScrn); + VBOXPtr pVBox = VBOXGetRec(pScrn); + int adjustedWidth = pScrn->bitsPerPixel == 16 ? (width + 1) & ~1 : width; + int cbLine = adjustedWidth * pScrn->bitsPerPixel / 8; + PixmapPtr pPixmap; + + TRACE_LOG("width=%d, height=%d\n", width, height); + AssertMsg(width >= 0 && height >= 0, ("Invalid negative width (%d) or height (%d)\n", width, height)); + if (pScreen == NULL) /* Not yet initialised. */ + return TRUE; + pPixmap = pScreen->GetScreenPixmap(pScreen); + AssertMsg(pPixmap != NULL, ("Failed to get the screen pixmap.\n")); + TRACE_LOG("pPixmap=%p adjustedWidth=%d height=%d pScrn->depth=%d pScrn->bitsPerPixel=%d cbLine=%d pVBox->base=%p pPixmap->drawable.width=%d pPixmap->drawable.height=%d\n", + (void *)pPixmap, adjustedWidth, height, pScrn->depth, + pScrn->bitsPerPixel, cbLine, pVBox->base, + pPixmap->drawable.width, pPixmap->drawable.height); + if ( adjustedWidth != pPixmap->drawable.width + || height != pPixmap->drawable.height) + { + if ( adjustedWidth > VBOX_VIDEO_MAX_VIRTUAL || height > VBOX_VIDEO_MAX_VIRTUAL + || (unsigned)cbLine * (unsigned)height >= pVBox->cbFBMax) + { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Virtual framebuffer %dx%d too large. For information, video memory: %u Kb.\n", + adjustedWidth, height, (unsigned) pVBox->cbFBMax / 1024); + return FALSE; + } + if (pScrn->vtSema) + vbvxClearVRAM(pScrn, ((size_t)pScrn->virtualX) * pScrn->virtualY * (pScrn->bitsPerPixel / 8), + ((size_t)adjustedWidth) * height * (pScrn->bitsPerPixel / 8)); + pScreen->ModifyPixmapHeader(pPixmap, adjustedWidth, height, pScrn->depth, pScrn->bitsPerPixel, cbLine, pVBox->base); + } + pScrn->displayWidth = pScrn->virtualX = adjustedWidth; + pScrn->virtualY = height; + return TRUE; +} + +#ifndef VBOXVIDEO_13 +/** Set a video mode to the hardware, RandR 1.1 version. + * + * Since we no longer do virtual frame buffers, adjust the screen pixmap + * dimensions to match. The "override" parameters are for when we received a + * mode hint while switched to a virtual terminal. In this case VBoxClient will + * have told us about the mode, but not yet been able to do a mode switch using + * RandR. We solve this by setting the requested mode to the host but keeping + * the virtual frame- + * buffer matching what the X server expects. */ +static void setModeRandR11(ScrnInfoPtr pScrn, DisplayModePtr pMode, Bool fScreenInitTime, Bool fEnterVTTime, + int cXOverRide, int cYOverRide) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + struct vbvxFrameBuffer frameBuffer = { 0, 0, pMode->HDisplay, pMode->VDisplay, pScrn->bitsPerPixel}; + int cXPhysical = cXOverRide > 0 ? min(cXOverRide, pMode->HDisplay) : pMode->HDisplay; + int cYPhysical = cYOverRide > 0 ? min(cYOverRide, pMode->VDisplay) : pMode->VDisplay; + + pVBox->pScreens[0].aScreenLocation.cx = pMode->HDisplay; + pVBox->pScreens[0].aScreenLocation.cy = pMode->VDisplay; + if (fScreenInitTime) + { + /* The screen structure is not fully set up yet, so do not touch it. */ + pScrn->displayWidth = pScrn->virtualX = pMode->HDisplay; + pScrn->virtualY = pMode->VDisplay; + } + else + { + xf86ScrnToScreen(pScrn)->width = pMode->HDisplay; + xf86ScrnToScreen(pScrn)->height = pMode->VDisplay; + /* This prevents a crash in CentOS 3. I was unable to debug it to + * satisfaction, partly due to the lack of symbols. My guess is that + * pScrn->ModifyPixmapHeader() expects certain things to be set up when + * it sees pScrn->vtSema set to true which are not quite done at this + * point of the VT switch. */ + if (fEnterVTTime) + pScrn->vtSema = FALSE; + adjustScreenPixmap(pScrn, pMode->HDisplay, pMode->VDisplay); + if (fEnterVTTime) + pScrn->vtSema = TRUE; + } + if (pMode->HDisplay != 0 && pMode->VDisplay != 0 && pScrn->vtSema) + vbvxSetMode(pScrn, 0, cXPhysical, cYPhysical, 0, 0, true, true, &frameBuffer); + pScrn->currentMode = pMode; +} +#endif + +#ifdef VBOXVIDEO_13 +/* X.org 1.3+ mode-setting support ******************************************/ + +/** Set a video mode to the hardware, RandR 1.2 version. If this is the first + * screen, re-set the current mode for all others (the offset for the first + * screen is always treated as zero by the hardware, so all other screens need + * to be changed to compensate for any changes!). The mode to set is taken + * from the X.Org Crtc structure. */ +static void setModeRandR12(ScrnInfoPtr pScrn, unsigned cScreen) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + unsigned i; + struct vbvxFrameBuffer frameBuffer = { pVBox->pScreens[0].paCrtcs->x, pVBox->pScreens[0].paCrtcs->y, pScrn->virtualX, + pScrn->virtualY, pScrn->bitsPerPixel }; + unsigned cFirst = cScreen; + unsigned cLast = cScreen != 0 ? cScreen + 1 : pVBox->cScreens; + int originalX, originalY; + + /* Check that this code cannot trigger the resizing bug in X.Org Server 1.3. + * See the work-around in ScreenInit. */ + xf86RandR12GetOriginalVirtualSize(pScrn, &originalX, &originalY); + AssertMsg(originalX == VBOX_VIDEO_MAX_VIRTUAL && originalY == VBOX_VIDEO_MAX_VIRTUAL, ("OriginalSize=%dx%d", + originalX, originalY)); + for (i = cFirst; i < cLast; ++i) + if (pVBox->pScreens[i].paCrtcs->mode.HDisplay != 0 && pVBox->pScreens[i].paCrtcs->mode.VDisplay != 0 && pScrn->vtSema) + vbvxSetMode(pScrn, i, pVBox->pScreens[i].paCrtcs->mode.HDisplay, pVBox->pScreens[i].paCrtcs->mode.VDisplay, + pVBox->pScreens[i].paCrtcs->x, pVBox->pScreens[i].paCrtcs->y, pVBox->pScreens[i].fPowerOn, + pVBox->pScreens[i].paOutputs->status == XF86OutputStatusConnected, &frameBuffer); +} + +/** Wrapper around setModeRandR12() to avoid exposing non-obvious semantics. + */ +static void setAllModesRandR12(ScrnInfoPtr pScrn) +{ + setModeRandR12(pScrn, 0); +} + +/* For descriptions of these functions and structures, see + hw/xfree86/modes/xf86Crtc.h and hw/xfree86/modes/xf86Modes.h in the + X.Org source tree. */ + +static Bool vbox_config_resize(ScrnInfoPtr pScrn, int cw, int ch) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + Bool rc; + unsigned i; + + TRACE_LOG("width=%d, height=%d\n", cw, ch); + rc = adjustScreenPixmap(pScrn, cw, ch); + /* Power-on all screens (the server expects this) and set the new pitch to them. */ + for (i = 0; i < pVBox->cScreens; ++i) + pVBox->pScreens[i].fPowerOn = true; + setAllModesRandR12(pScrn); + vbvxSetSolarisMouseRange(cw, ch); + return rc; +} + +static const xf86CrtcConfigFuncsRec VBOXCrtcConfigFuncs = { + vbox_config_resize +}; + +static void +vbox_crtc_dpms(xf86CrtcPtr crtc, int mode) +{ + ScrnInfoPtr pScrn = crtc->scrn; + VBOXPtr pVBox = VBOXGetRec(pScrn); + unsigned cDisplay = (uintptr_t)crtc->driver_private; + + TRACE_LOG("mode=%d\n", mode); + pVBox->pScreens[cDisplay].fPowerOn = (mode != DPMSModeOff); + setModeRandR12(pScrn, cDisplay); +} + +static Bool +vbox_crtc_lock (xf86CrtcPtr crtc) +{ RT_NOREF(crtc); return FALSE; } + + +/* We use this function to check whether the X server owns the active virtual + * terminal before attempting a mode switch, since the RandR extension isn't + * very dilligent here, which can mean crashes if we are unlucky. This is + * not the way it the function is intended - it is meant for reporting modes + * which the hardware can't handle. I hope that this won't confuse any clients + * connecting to us. */ +static Bool +vbox_crtc_mode_fixup (xf86CrtcPtr crtc, DisplayModePtr mode, + DisplayModePtr adjusted_mode) +{ RT_NOREF(crtc, mode, adjusted_mode); return TRUE; } + +static void +vbox_crtc_stub (xf86CrtcPtr crtc) +{ RT_NOREF(crtc); } + +static void +vbox_crtc_mode_set (xf86CrtcPtr crtc, DisplayModePtr mode, + DisplayModePtr adjusted_mode, int x, int y) +{ + RT_NOREF(mode); + VBOXPtr pVBox = VBOXGetRec(crtc->scrn); + unsigned cDisplay = (uintptr_t)crtc->driver_private; + + TRACE_LOG("name=%s, HDisplay=%d, VDisplay=%d, x=%d, y=%d\n", adjusted_mode->name, + adjusted_mode->HDisplay, adjusted_mode->VDisplay, x, y); + pVBox->pScreens[cDisplay].fPowerOn = true; + pVBox->pScreens[cDisplay].aScreenLocation.cx = adjusted_mode->HDisplay; + pVBox->pScreens[cDisplay].aScreenLocation.cy = adjusted_mode->VDisplay; + pVBox->pScreens[cDisplay].aScreenLocation.x = x; + pVBox->pScreens[cDisplay].aScreenLocation.y = y; + setModeRandR12(crtc->scrn, cDisplay); +} + +static void +vbox_crtc_gamma_set (xf86CrtcPtr crtc, CARD16 *red, + CARD16 *green, CARD16 *blue, int size) +{ RT_NOREF(crtc, red, green, blue, size); } + +static void * +vbox_crtc_shadow_allocate (xf86CrtcPtr crtc, int width, int height) +{ RT_NOREF(crtc, width, height); return NULL; } + +static const xf86CrtcFuncsRec VBOXCrtcFuncs = { + .dpms = vbox_crtc_dpms, + .save = NULL, /* These two are never called by the server. */ + .restore = NULL, + .lock = vbox_crtc_lock, + .unlock = NULL, /* This will not be invoked if lock returns FALSE. */ + .mode_fixup = vbox_crtc_mode_fixup, + .prepare = vbox_crtc_stub, + .mode_set = vbox_crtc_mode_set, + .commit = vbox_crtc_stub, + .gamma_set = vbox_crtc_gamma_set, + .shadow_allocate = vbox_crtc_shadow_allocate, + .shadow_create = NULL, /* These two should not be invoked if allocate + returns NULL. */ + .shadow_destroy = NULL, + .set_cursor_colors = NULL, /* We are still using the old cursor API. */ + .set_cursor_position = NULL, + .show_cursor = NULL, + .hide_cursor = NULL, + .load_cursor_argb = NULL, + .destroy = vbox_crtc_stub +}; + +static void +vbox_output_stub (xf86OutputPtr output) +{ RT_NOREF(output); } + +static void +vbox_output_dpms (xf86OutputPtr output, int mode) +{ + RT_NOREF(output, mode); +} + +static int +vbox_output_mode_valid (xf86OutputPtr output, DisplayModePtr mode) +{ + return MODE_OK; +} + +static Bool +vbox_output_mode_fixup (xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) +{ RT_NOREF(output, mode, adjusted_mode); return TRUE; } + +static void +vbox_output_mode_set (xf86OutputPtr output, DisplayModePtr mode, + DisplayModePtr adjusted_mode) +{ RT_NOREF(output, mode, adjusted_mode); } + +static xf86OutputStatus +vbox_output_detect (xf86OutputPtr output) +{ + ScrnInfoPtr pScrn = output->scrn; + VBOXPtr pVBox = VBOXGetRec(pScrn); + uint32_t iScreen = (uintptr_t)output->driver_private; + return pVBox->pScreens[iScreen].afConnected + ? XF86OutputStatusConnected : XF86OutputStatusDisconnected; +} + +static DisplayModePtr vbox_output_add_mode(VBOXPtr pVBox, DisplayModePtr *pModes, const char *pszName, int x, int y, + Bool isPreferred, Bool isUserDef) +{ + TRACE_LOG("pszName=%s, x=%d, y=%d\n", pszName ? pszName : "(null)", x, y); + DisplayModePtr pMode = xnfcalloc(1, sizeof(DisplayModeRec)); + int cRefresh = 60; + + pMode->status = MODE_OK; + /* We don't ask the host whether it likes user defined modes, + * as we assume that the user really wanted that mode. */ + pMode->type = isUserDef ? M_T_USERDEF : M_T_BUILTIN; + if (isPreferred) + pMode->type |= M_T_PREFERRED; + /* Older versions of VBox only support screen widths which are a multiple + * of 8 */ + if (pVBox->fAnyX) + pMode->HDisplay = x; + else + pMode->HDisplay = x & ~7; + pMode->HSyncStart = pMode->HDisplay + 2; + pMode->HSyncEnd = pMode->HDisplay + 4; + pMode->HTotal = pMode->HDisplay + 6; + pMode->VDisplay = y; + pMode->VSyncStart = pMode->VDisplay + 2; + pMode->VSyncEnd = pMode->VDisplay + 4; + pMode->VTotal = pMode->VDisplay + 6; + pMode->Clock = pMode->HTotal * pMode->VTotal * cRefresh / 1000; /* kHz */ + if (NULL == pszName) { + xf86SetModeDefaultName(pMode); + } else { + pMode->name = xnfstrdup(pszName); + } + *pModes = xf86ModesAdd(*pModes, pMode); + return pMode; +} + +static DisplayModePtr +vbox_output_get_modes (xf86OutputPtr output) +{ + DisplayModePtr pModes = NULL; + DisplayModePtr pPreferred = NULL; + ScrnInfoPtr pScrn = output->scrn; + VBOXPtr pVBox = VBOXGetRec(pScrn); + + TRACE_ENTRY(); + uint32_t iScreen = (uintptr_t)output->driver_private; + pPreferred = vbox_output_add_mode(pVBox, &pModes, NULL, + RT_CLAMP(pVBox->pScreens[iScreen].aPreferredSize.cx, VBOX_VIDEO_MIN_SIZE, VBOX_VIDEO_MAX_VIRTUAL), + RT_CLAMP(pVBox->pScreens[iScreen].aPreferredSize.cy, VBOX_VIDEO_MIN_SIZE, VBOX_VIDEO_MAX_VIRTUAL), + TRUE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 2560, 1600, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 2560, 1440, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 2048, 1536, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 1920, 1600, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 1920, 1080, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 1680, 1050, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 1600, 1200, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 1400, 1050, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 1280, 1024, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 1024, 768, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 800, 600, FALSE, FALSE); + vbox_output_add_mode(pVBox, &pModes, NULL, 640, 480, FALSE, FALSE); + VBOXEDIDSet(output, pPreferred); + TRACE_EXIT(); + return pModes; +} + +static const xf86OutputFuncsRec VBOXOutputFuncs = { + .create_resources = vbox_output_stub, + .dpms = vbox_output_dpms, + .save = NULL, /* These two are never called by the server. */ + .restore = NULL, + .mode_valid = vbox_output_mode_valid, + .mode_fixup = vbox_output_mode_fixup, + .prepare = vbox_output_stub, + .commit = vbox_output_stub, + .mode_set = vbox_output_mode_set, + .detect = vbox_output_detect, + .get_modes = vbox_output_get_modes, +#ifdef RANDR_12_INTERFACE + .set_property = NULL, +#endif + .destroy = vbox_output_stub +}; +#endif /* VBOXVIDEO_13 */ + +/* Module loader interface */ +static MODULESETUPPROTO(vboxSetup); + +static XF86ModuleVersionInfo vboxVersionRec = +{ + VBOX_DRIVER_NAME, + "Oracle Corporation", + MODINFOSTRING1, + MODINFOSTRING2, +#ifdef XORG_7X + XORG_VERSION_CURRENT, +#else + XF86_VERSION_CURRENT, +#endif + 1, /* Module major version. Xorg-specific */ + 0, /* Module minor version. Xorg-specific */ + 1, /* Module patchlevel. Xorg-specific */ + ABI_CLASS_VIDEODRV, /* This is a video driver */ + ABI_VIDEODRV_VERSION, + MOD_CLASS_VIDEODRV, + {0, 0, 0, 0} +}; + +/* + * This data is accessed by the loader. The name must be the module name + * followed by "ModuleData". + */ +#ifdef XORG_7X +_X_EXPORT +#endif +XF86ModuleData vboxvideoModuleData = { &vboxVersionRec, vboxSetup, NULL }; + +static pointer +vboxSetup(pointer Module, pointer Options, int *ErrorMajor, int *ErrorMinor) +{ + static Bool Initialised = FALSE; + RT_NOREF(Options, ErrorMinor); + + if (!Initialised) + { + Initialised = TRUE; +#ifdef PCIACCESS + xf86AddDriver(&VBOXVIDEO, Module, HaveDriverFuncs); +#else + xf86AddDriver(&VBOXVIDEO, Module, 0); +#endif +#ifndef XORG_7X + LoaderRefSymLists(fbSymbols, + shadowfbSymbols, + ramdacSymbols, + vgahwSymbols, + NULL); +#endif + xf86Msg(X_CONFIG, "Load address of symbol \"VBOXVIDEO\" is %p\n", + (void *)&VBOXVIDEO); + return (pointer)TRUE; + } + + if (ErrorMajor) + *ErrorMajor = LDR_ONCEONLY; + return (NULL); +} + + +static const OptionInfoRec * +VBOXAvailableOptions(int chipid, int busid) +{ + RT_NOREF(chipid, busid); + return (VBOXOptions); +} + +static void +VBOXIdentify(int flags) +{ + RT_NOREF(flags); + xf86PrintChipsets(VBOX_NAME, "guest driver for VirtualBox", VBOXChipsets); +} + +#ifndef XF86_SCRN_INTERFACE +# define SCRNINDEXAPI(pfn) pfn ## Index +static Bool VBOXScreenInitIndex(int scrnIndex, ScreenPtr pScreen, int argc, char **argv) +{ + RT_NOREF(scrnIndex); + return VBOXScreenInit(pScreen, argc, argv); +} + +static Bool VBOXEnterVTIndex(int scrnIndex, int flags) +{ RT_NOREF(flags); return VBOXEnterVT(xf86Screens[scrnIndex]); } + +static void VBOXLeaveVTIndex(int scrnIndex, int flags) +{ RT_NOREF(flags); VBOXLeaveVT(xf86Screens[scrnIndex]); } + +static Bool VBOXCloseScreenIndex(int scrnIndex, ScreenPtr pScreen) +{ RT_NOREF(scrnIndex); return VBOXCloseScreen(pScreen); } + +static Bool VBOXSwitchModeIndex(int scrnIndex, DisplayModePtr pMode, int flags) +{ RT_NOREF(flags); return VBOXSwitchMode(xf86Screens[scrnIndex], pMode); } + +static void VBOXAdjustFrameIndex(int scrnIndex, int x, int y, int flags) +{ RT_NOREF(flags); VBOXAdjustFrame(xf86Screens[scrnIndex], x, y); } + +static void VBOXFreeScreenIndex(int scrnIndex, int flags) +{ RT_NOREF(flags); VBOXFreeScreen(xf86Screens[scrnIndex]); } +# else +# define SCRNINDEXAPI(pfn) pfn +#endif /* XF86_SCRN_INTERFACE */ + +static void setScreenFunctions(ScrnInfoPtr pScrn, xf86ProbeProc pfnProbe) +{ + pScrn->driverVersion = VBOX_VERSION; + pScrn->driverName = VBOX_DRIVER_NAME; + pScrn->name = VBOX_NAME; + pScrn->Probe = pfnProbe; + pScrn->PreInit = VBOXPreInit; + pScrn->ScreenInit = SCRNINDEXAPI(VBOXScreenInit); + pScrn->SwitchMode = SCRNINDEXAPI(VBOXSwitchMode); + pScrn->AdjustFrame = SCRNINDEXAPI(VBOXAdjustFrame); + pScrn->EnterVT = SCRNINDEXAPI(VBOXEnterVT); + pScrn->LeaveVT = SCRNINDEXAPI(VBOXLeaveVT); + pScrn->FreeScreen = SCRNINDEXAPI(VBOXFreeScreen); +} + +/* + * One of these functions is called once, at the start of the first server + * generation to do a minimal probe for supported hardware. + */ + +#ifdef PCIACCESS +static Bool +VBOXPciProbe(DriverPtr drv, int entity_num, struct pci_device *dev, + intptr_t match_data) +{ + ScrnInfoPtr pScrn; + int drmFd; + + TRACE_ENTRY(); + + drmFd = open("/dev/dri/card0", O_RDWR, 0); + if (drmFd >= 0) + { + xf86Msg(X_INFO, "vboxvideo: kernel driver found, not loading.\n"); + close(drmFd); + return FALSE; + } + /* It is safe to call this, as the X server enables I/O access before + * calling the probe call-backs. */ + if (!xf86EnableIO()) + { + xf86Msg(X_INFO, "vboxvideo: this driver requires direct hardware access. You may wish to use the kernel driver instead.\n"); + return FALSE; + } + pScrn = xf86ConfigPciEntity(NULL, 0, entity_num, VBOXPCIchipsets, + NULL, NULL, NULL, NULL, NULL); + if (pScrn != NULL) { + VBOXPtr pVBox; + + VBOXSetRec(pScrn); + pVBox = VBOXGetRec(pScrn); + if (!pVBox) + return FALSE; + setScreenFunctions(pScrn, NULL); + pVBox->pciInfo = dev; + } + + TRACE_LOG("returning %s\n", pScrn == NULL ? "false" : "true"); + return (pScrn != NULL); +} +#endif + +#ifndef PCIACCESS +static Bool +VBOXProbe(DriverPtr drv, int flags) +{ + Bool foundScreen = FALSE; + int numDevSections; + GDevPtr *devSections; + + /* + * Find the config file Device sections that match this + * driver, and return if there are none. + */ + if ((numDevSections = xf86MatchDevice(VBOX_NAME, + &devSections)) <= 0) + return (FALSE); + + /* PCI BUS */ + if (xf86GetPciVideoInfo()) + { + int numUsed; + int *usedChips; + int i; + numUsed = xf86MatchPciInstances(VBOX_NAME, VBOX_VENDORID, + VBOXChipsets, VBOXPCIchipsets, + devSections, numDevSections, + drv, &usedChips); + if (numUsed > 0) + { + if (flags & PROBE_DETECT) + foundScreen = TRUE; + else + for (i = 0; i < numUsed; i++) + { + ScrnInfoPtr pScrn = NULL; + /* Allocate a ScrnInfoRec */ + if ((pScrn = xf86ConfigPciEntity(pScrn,0,usedChips[i], + VBOXPCIchipsets,NULL, + NULL,NULL,NULL,NULL))) + { + setScreenFunctions(pScrn, VBOXProbe); + foundScreen = TRUE; + } + } + free(usedChips); + } + } + free(devSections); + return (foundScreen); +} +#endif + + +/* + * QUOTE from the XFree86 DESIGN document: + * + * The purpose of this function is to find out all the information + * required to determine if the configuration is usable, and to initialise + * those parts of the ScrnInfoRec that can be set once at the beginning of + * the first server generation. + * + * (...) + * + * This includes probing for video memory, clocks, ramdac, and all other + * HW info that is needed. It includes determining the depth/bpp/visual + * and related info. It includes validating and determining the set of + * video modes that will be used (and anything that is required to + * determine that). + * + * This information should be determined in the least intrusive way + * possible. The state of the HW must remain unchanged by this function. + * Although video memory (including MMIO) may be mapped within this + * function, it must be unmapped before returning. + * + * END QUOTE + */ + +static Bool +VBOXPreInit(ScrnInfoPtr pScrn, int flags) +{ + VBOXPtr pVBox; + Gamma gzeros = {0.0, 0.0, 0.0}; + rgb rzeros = {0, 0, 0}; + + TRACE_ENTRY(); + /* Are we really starting the server, or is this just a dummy run? */ + if (flags & PROBE_DETECT) + return (FALSE); + + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "VirtualBox guest additions video driver version %d.%d\n", + VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR); + + /* The ramdac module is needed for the hardware cursor. */ + if (!xf86LoadSubModule(pScrn, "ramdac")) + return FALSE; + + /* The framebuffer module. */ + if (!xf86LoadSubModule(pScrn, "fb")) + return (FALSE); + + if (!xf86LoadSubModule(pScrn, "shadowfb")) + return FALSE; + + if (!xf86LoadSubModule(pScrn, "vgahw")) + return FALSE; + + /* Get our private data from the ScrnInfoRec structure. */ + VBOXSetRec(pScrn); + pVBox = VBOXGetRec(pScrn); + if (!pVBox) + return FALSE; + + /* Entity information seems to mean bus information. */ + pVBox->pEnt = xf86GetEntityInfo(pScrn->entityList[0]); + +#ifndef PCIACCESS + if (pVBox->pEnt->location.type != BUS_PCI) + return FALSE; + + pVBox->pciInfo = xf86GetPciInfoForEntity(pVBox->pEnt->index); + pVBox->pciTag = pciTag(pVBox->pciInfo->bus, + pVBox->pciInfo->device, + pVBox->pciInfo->func); +#endif + + /* Set up our ScrnInfoRec structure to describe our virtual + capabilities to X. */ + + pScrn->chipset = "vbox"; + /** @note needed during colourmap initialisation */ + pScrn->rgbBits = 8; + + /* Let's create a nice, capable virtual monitor. */ + pScrn->monitor = pScrn->confScreen->monitor; + pScrn->monitor->DDC = NULL; + pScrn->monitor->nHsync = 1; + pScrn->monitor->hsync[0].lo = 1; + pScrn->monitor->hsync[0].hi = 10000; + pScrn->monitor->nVrefresh = 1; + pScrn->monitor->vrefresh[0].lo = 1; + pScrn->monitor->vrefresh[0].hi = 100; + + pScrn->progClock = TRUE; + + /* Using the PCI information caused problems with non-powers-of-two + sized video RAM configurations */ + pVBox->cbFBMax = VBoxVideoGetVRAMSize(); + pScrn->videoRam = pVBox->cbFBMax / 1024; + + /* Check if the chip restricts horizontal resolution or not. */ + pVBox->fAnyX = VBoxVideoAnyWidthAllowed(); + + /* Set up clock information that will support all modes we need. */ + pScrn->clockRanges = xnfcalloc(sizeof(ClockRange), 1); + pScrn->clockRanges->minClock = 1000; + pScrn->clockRanges->maxClock = 1000000000; + pScrn->clockRanges->clockIndex = -1; + pScrn->clockRanges->ClockMulFactor = 1; + pScrn->clockRanges->ClockDivFactor = 1; + + if (!xf86SetDepthBpp(pScrn, 24, 0, 0, Support32bppFb)) + return FALSE; + /* We only support 16 and 24 bits depth (i.e. 16 and 32bpp) */ + if (pScrn->bitsPerPixel != 32 && pScrn->bitsPerPixel != 16) + { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "The VBox additions only support 16 and 32bpp graphics modes\n"); + return FALSE; + } + xf86PrintDepthBpp(pScrn); + vboxAddModes(pScrn); + +#ifdef VBOXVIDEO_13 + pScrn->virtualX = VBOX_VIDEO_MAX_VIRTUAL; + pScrn->virtualY = VBOX_VIDEO_MAX_VIRTUAL; +#else + /* We don't validate with xf86ValidateModes and xf86PruneModes as we + * already know what we like and what we don't. */ + + pScrn->currentMode = pScrn->modes; + + /* Set the right virtual resolution. */ + pScrn->virtualX = pScrn->bitsPerPixel == 16 ? (pScrn->currentMode->HDisplay + 1) & ~1 : pScrn->currentMode->HDisplay; + pScrn->virtualY = pScrn->currentMode->VDisplay; + +#endif /* !VBOXVIDEO_13 */ + + pScrn->displayWidth = pScrn->virtualX; + + xf86PrintModes(pScrn); + + /* VGA hardware initialisation */ + if (!vgaHWGetHWRec(pScrn)) + return FALSE; + /* Must be called before any VGA registers are saved or restored */ + vgaHWSetStdFuncs(VGAHWPTR(pScrn)); + vgaHWGetIOBase(VGAHWPTR(pScrn)); + + /* Colour weight - we always call this, since we are always in + truecolour. */ + if (!xf86SetWeight(pScrn, rzeros, rzeros)) + return (FALSE); + + /* visual init */ + if (!xf86SetDefaultVisual(pScrn, -1)) + return (FALSE); + + xf86SetGamma(pScrn, gzeros); + + /* Set the DPI. Perhaps we should read this from the host? */ + xf86SetDpi(pScrn, 96, 96); + + if (pScrn->memPhysBase == 0) { +#ifdef PCIACCESS + pScrn->memPhysBase = pVBox->pciInfo->regions[0].base_addr; +#else + pScrn->memPhysBase = pVBox->pciInfo->memBase[0]; +#endif + pScrn->fbOffset = 0; + } + + TRACE_EXIT(); + return (TRUE); +} + +/** + * Dummy function for setting the colour palette, which we actually never + * touch. However, the server still requires us to provide this. + */ +static void +vboxLoadPalette(ScrnInfoPtr pScrn, int numColors, int *indices, + LOCO *colors, VisualPtr pVisual) +{ + RT_NOREF(pScrn, numColors, indices, colors, pVisual); +} + +/** Set the graphics and guest cursor support capabilities to the host if + * the user-space helper is running. */ +static void updateGraphicsCapability(ScrnInfoPtr pScrn, Bool hasVT) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + + if (!pVBox->fHaveHGSMIModeHints) + return; + VBoxHGSMISendCapsInfo(&pVBox->guestCtx, hasVT + ? VBVACAPS_VIDEO_MODE_HINTS | VBVACAPS_DISABLE_CURSOR_INTEGRATION + : VBVACAPS_DISABLE_CURSOR_INTEGRATION); +} + +#ifndef VBOXVIDEO_13 + +#define PREFERRED_MODE_ATOM_NAME "VBOXVIDEO_PREFERRED_MODE" + +static void setSizesRandR11(ScrnInfoPtr pScrn) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + DisplayModePtr pNewMode; + int32_t propertyValue; + + pNewMode = pScrn->modes != pScrn->currentMode ? pScrn->modes : pScrn->modes->next; + pNewMode->HDisplay = RT_CLAMP(pVBox->pScreens[0].aPreferredSize.cx, VBOX_VIDEO_MIN_SIZE, VBOX_VIDEO_MAX_VIRTUAL); + pNewMode->VDisplay = RT_CLAMP(pVBox->pScreens[0].aPreferredSize.cy, VBOX_VIDEO_MIN_SIZE, VBOX_VIDEO_MAX_VIRTUAL); + propertyValue = (pNewMode->HDisplay << 16) + pNewMode->VDisplay; + ChangeWindowProperty(ROOT_WINDOW(pScrn), MakeAtom(PREFERRED_MODE_ATOM_NAME, + sizeof(PREFERRED_MODE_ATOM_NAME) - 1, TRUE), XA_INTEGER, 32, + PropModeReplace, 1, &propertyValue, TRUE); +} + +#endif + +static void reprobeCursor(ScrnInfoPtr pScrn) +{ + if (ROOT_WINDOW(pScrn) == NULL) + return; +#ifdef XF86_SCRN_INTERFACE + pScrn->EnableDisableFBAccess(pScrn, FALSE); + pScrn->EnableDisableFBAccess(pScrn, TRUE); +#else + pScrn->EnableDisableFBAccess(pScrn->scrnIndex, FALSE); + pScrn->EnableDisableFBAccess(pScrn->scrnIndex, TRUE); +#endif +} + +static void setSizesAndCursorIntegration(ScrnInfoPtr pScrn, Bool fScreenInitTime) +{ + RT_NOREF(fScreenInitTime); + TRACE_LOG("fScreenInitTime=%d\n", (int)fScreenInitTime); +#ifdef VBOXVIDEO_13 +# if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) >= 5 + RRGetInfo(xf86ScrnToScreen(pScrn), TRUE); +# else + RRGetInfo(xf86ScrnToScreen(pScrn)); +# endif +#else + setSizesRandR11(pScrn); +#endif + /* This calls EnableDisableFBAccess(), so only use when switched in. */ + if (pScrn->vtSema) + reprobeCursor(pScrn); +} + +/* We update the size hints from the X11 property set by VBoxClient every time + * that the X server goes to sleep (to catch the property change request). + * Although this is far more often than necessary it should not have real-life + * performance consequences and allows us to simplify the code quite a bit. */ +static void vboxBlockHandler(pointer pData, +#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 23 + OSTimePtr pTimeout, + pointer pReadmask +#else + void *pTimeout +#endif + ) +{ + ScrnInfoPtr pScrn = (ScrnInfoPtr)pData; + Bool fNeedUpdate = false; + + RT_NOREF(pTimeout); +#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 23 + RT_NOREF(pReadmask); +#endif + if (pScrn->vtSema) + vbvxReadSizesAndCursorIntegrationFromHGSMI(pScrn, &fNeedUpdate); + if (fNeedUpdate) + setSizesAndCursorIntegration(pScrn, false); +} + +/* + * QUOTE from the XFree86 DESIGN document: + * + * This is called at the start of each server generation. + * + * (...) + * + * Decide which operations need to be placed under resource access + * control. (...) Map any video memory or other memory regions. (...) + * Save the video card state. (...) Initialise the initial video + * mode. + * + * End QUOTE. + */ +static Bool VBOXScreenInit(ScreenPtr pScreen, int argc, char **argv) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); + VBOXPtr pVBox = VBOXGetRec(pScrn); + VisualPtr visual; + RT_NOREF(argc, argv); + + TRACE_ENTRY(); + + if (!VBOXMapVidMem(pScrn)) + return (FALSE); + + /* save current video state */ + VBOXSaveMode(pScrn); + + /* mi layer - reset the visual list (?)*/ + miClearVisualTypes(); + if (!miSetVisualTypes(pScrn->depth, TrueColorMask, + pScrn->rgbBits, TrueColor)) + return (FALSE); + if (!miSetPixmapDepths()) + return (FALSE); + + if (!fbScreenInit(pScreen, pVBox->base, + pScrn->virtualX, pScrn->virtualY, + pScrn->xDpi, pScrn->yDpi, + pScrn->displayWidth, pScrn->bitsPerPixel)) + return (FALSE); + + /* Fixup RGB ordering */ + /** @note the X server uses this even in true colour. */ + visual = pScreen->visuals + pScreen->numVisuals; + while (--visual >= pScreen->visuals) { + if ((visual->class | DynamicClass) == DirectColor) { + visual->offsetRed = pScrn->offset.red; + visual->offsetGreen = pScrn->offset.green; + visual->offsetBlue = pScrn->offset.blue; + visual->redMask = pScrn->mask.red; + visual->greenMask = pScrn->mask.green; + visual->blueMask = pScrn->mask.blue; + } + } + + /* must be after RGB ordering fixed */ + fbPictureInit(pScreen, 0, 0); + + xf86SetBlackWhitePixels(pScreen); + pScrn->vtSema = TRUE; + +#if defined(VBOXVIDEO_13) && defined(RT_OS_LINUX) + vbvxSetUpLinuxACPI(pScreen); +#endif + + if (!VBoxHGSMIIsSupported()) + { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Graphics device too old to support.\n"); + return FALSE; + } + vbvxSetUpHGSMIHeapInGuest(pVBox, pScrn->videoRam * 1024); + pVBox->cScreens = VBoxHGSMIGetMonitorCount(&pVBox->guestCtx); + pVBox->pScreens = xnfcalloc(pVBox->cScreens, sizeof(*pVBox->pScreens)); + pVBox->paVBVAModeHints = xnfcalloc(pVBox->cScreens, sizeof(*pVBox->paVBVAModeHints)); + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Requested monitor count: %u\n", pVBox->cScreens); + vboxEnableVbva(pScrn); + /* Set up the dirty rectangle handler. It will be added into a function + * chain and gets removed when the screen is cleaned up. */ + if (ShadowFBInit2(pScreen, NULL, vbvxHandleDirtyRect) != TRUE) + return FALSE; + VBoxInitialiseSizeHints(pScrn); + +#ifdef VBOXVIDEO_13 + /* Initialise CRTC and output configuration for use with randr1.2. */ + xf86CrtcConfigInit(pScrn, &VBOXCrtcConfigFuncs); + + { + uint32_t i; + + for (i = 0; i < pVBox->cScreens; ++i) + { + char szOutput[256]; + + /* Setup our virtual CRTCs. */ + pVBox->pScreens[i].paCrtcs = xf86CrtcCreate(pScrn, &VBOXCrtcFuncs); + pVBox->pScreens[i].paCrtcs->driver_private = (void *)(uintptr_t)i; + + /* Set up our virtual outputs. */ + snprintf(szOutput, sizeof(szOutput), "VGA-%u", i); + pVBox->pScreens[i].paOutputs + = xf86OutputCreate(pScrn, &VBOXOutputFuncs, szOutput); + + /* We are not interested in the monitor section in the + * configuration file. */ + xf86OutputUseScreenMonitor(pVBox->pScreens[i].paOutputs, FALSE); + pVBox->pScreens[i].paOutputs->possible_crtcs = 1 << i; + pVBox->pScreens[i].paOutputs->possible_clones = 0; + pVBox->pScreens[i].paOutputs->driver_private = (void *)(uintptr_t)i; + TRACE_LOG("Created crtc (%p) and output %s (%p)\n", + (void *)pVBox->pScreens[i].paCrtcs, szOutput, + (void *)pVBox->pScreens[i].paOutputs); + } + } + + /* Set a sane minimum and maximum mode size to match what the hardware + * supports. */ + xf86CrtcSetSizeRange(pScrn, VBOX_VIDEO_MIN_SIZE, VBOX_VIDEO_MIN_SIZE, VBOX_VIDEO_MAX_VIRTUAL, VBOX_VIDEO_MAX_VIRTUAL); + + /* Now create our initial CRTC/output configuration. */ + if (!xf86InitialConfiguration(pScrn, TRUE)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Initial CRTC configuration failed!\n"); + return (FALSE); + } + + /* Work around a bug in the original X server modesetting code, which took + * the first valid values set to these two as maxima over the server + * lifetime. This bug was introduced on Feb 15 2007 and was fixed in commit + * fa877d7f three months later, so it was present in X.Org Server 1.3. */ + pScrn->virtualX = VBOX_VIDEO_MAX_VIRTUAL; + pScrn->virtualY = VBOX_VIDEO_MAX_VIRTUAL; + + /* Initialise randr 1.2 mode-setting functions. */ + if (!xf86CrtcScreenInit(pScreen)) { + return FALSE; + } + + /* set first video mode */ + if (!xf86SetDesiredModes(pScrn)) { + return FALSE; + } +#else /* !VBOXVIDEO_13 */ + /* set first video mode */ + setModeRandR11(pScrn, pScrn->currentMode, true, false, 0, 0); +#endif /* !VBOXVIDEO_13 */ + + /* Say that we support graphics. */ + updateGraphicsCapability(pScrn, TRUE); + +#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) >= 23 +# define WakeupHandlerProcPtr ServerWakeupHandlerProcPtr +#endif + + /* Register block and wake-up handlers for getting new screen size hints. */ + RegisterBlockAndWakeupHandlers(vboxBlockHandler, (WakeupHandlerProcPtr)NoopDDA, (pointer)pScrn); + + /* software cursor */ + miDCInitialize(pScreen, xf86GetPointerScreenFuncs()); + + /* colourmap code */ + if (!miCreateDefColormap(pScreen)) + return (FALSE); + + if(!xf86HandleColormaps(pScreen, 256, 8, vboxLoadPalette, NULL, 0)) + return (FALSE); + + pVBox->CloseScreen = pScreen->CloseScreen; + pScreen->CloseScreen = SCRNINDEXAPI(VBOXCloseScreen); +#ifdef VBOXVIDEO_13 + pScreen->SaveScreen = xf86SaveScreen; +#else + pScreen->SaveScreen = VBOXSaveScreen; +#endif + +#ifdef VBOXVIDEO_13 + xf86DPMSInit(pScreen, xf86DPMSSet, 0); +#else + /* We probably do want to support power management - even if we just use + a dummy function. */ + xf86DPMSInit(pScreen, VBOXDisplayPowerManagementSet, 0); +#endif + + /* Report any unused options (only for the first generation) */ + if (serverGeneration == 1) + xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options); + + if (vbvxCursorInit(pScreen) != TRUE) + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Unable to start the VirtualBox mouse pointer integration with the host system.\n"); + + return (TRUE); +} + +#define NO_VT_ATOM_NAME "VBOXVIDEO_NO_VT" + +static Bool VBOXEnterVT(ScrnInfoPtr pScrn) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); +#ifndef VBOXVIDEO_13 + /* If we got a mode request while we were switched out, temporarily override + * the physical mode set to the device while keeping things consistent from + * the server's point of view. */ + int cXOverRide = RT_CLAMP(pVBox->pScreens[0].aPreferredSize.cx, VBOX_VIDEO_MIN_SIZE, VBOX_VIDEO_MAX_VIRTUAL); + int cYOverRide = RT_CLAMP(pVBox->pScreens[0].aPreferredSize.cy, VBOX_VIDEO_MIN_SIZE, VBOX_VIDEO_MAX_VIRTUAL); +#endif + + TRACE_ENTRY(); + vbvxSetUpHGSMIHeapInGuest(pVBox, pScrn->videoRam * 1024); + vboxEnableVbva(pScrn); + /* Re-set video mode */ +#ifdef VBOXVIDEO_13 + if (!xf86SetDesiredModes(pScrn)) { + return FALSE; + } +#else + setModeRandR11(pScrn, pScrn->currentMode, false, true, cXOverRide, cYOverRide); + DeleteProperty(ROOT_WINDOW(pScrn), MakeAtom(NO_VT_ATOM_NAME, sizeof(NO_VT_ATOM_NAME) - 1, TRUE)); +#endif + updateGraphicsCapability(pScrn, TRUE); + return TRUE; +} + +static void VBOXLeaveVT(ScrnInfoPtr pScrn) +{ +#ifdef VBOXVIDEO_13 + VBOXPtr pVBox = VBOXGetRec(pScrn); + unsigned i; +#else + int32_t propertyValue = 0; +#endif + + TRACE_ENTRY(); +#ifdef VBOXVIDEO_13 + for (i = 0; i < pVBox->cScreens; ++i) + vbox_crtc_dpms(pVBox->pScreens[i].paCrtcs, DPMSModeOff); +#else + ChangeWindowProperty(ROOT_WINDOW(pScrn), MakeAtom(NO_VT_ATOM_NAME, sizeof(NO_VT_ATOM_NAME) - 1, FALSE), XA_INTEGER, 32, + PropModeReplace, 1, &propertyValue, TRUE); +#endif + updateGraphicsCapability(pScrn, FALSE); + vboxDisableVbva(pScrn); + vbvxClearVRAM(pScrn, ((size_t)pScrn->virtualX) * pScrn->virtualY * (pScrn->bitsPerPixel / 8), 0); + VBOXRestoreMode(pScrn); + TRACE_EXIT(); +} + +static Bool VBOXCloseScreen(ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen); + VBOXPtr pVBox = VBOXGetRec(pScrn); + BOOL ret; + + if (pScrn->vtSema) + { +#ifdef VBOXVIDEO_13 + unsigned i; + + for (i = 0; i < pVBox->cScreens; ++i) + vbox_crtc_dpms(pVBox->pScreens[i].paCrtcs, DPMSModeOff); +#endif + vboxDisableVbva(pScrn); + vbvxClearVRAM(pScrn, ((size_t)pScrn->virtualX) * pScrn->virtualY * (pScrn->bitsPerPixel / 8), 0); + } + if (pScrn->vtSema) + VBOXRestoreMode(pScrn); + if (pScrn->vtSema) + VBOXUnmapVidMem(pScrn); + pScrn->vtSema = FALSE; + + vbvxCursorTerm(pVBox); + + pScreen->CloseScreen = pVBox->CloseScreen; +#if defined(VBOXVIDEO_13) && defined(RT_OS_LINUX) + vbvxCleanUpLinuxACPI(pScreen); +#endif +#ifndef XF86_SCRN_INTERFACE + ret = pScreen->CloseScreen(pScreen->myNum, pScreen); +#else + ret = pScreen->CloseScreen(pScreen); +#endif + return ret; +} + +static Bool VBOXSwitchMode(ScrnInfoPtr pScrn, DisplayModePtr pMode) +{ + Bool rc = TRUE; + + TRACE_LOG("HDisplay=%d, VDisplay=%d\n", pMode->HDisplay, pMode->VDisplay); +#ifdef VBOXVIDEO_13 + rc = xf86SetSingleMode(pScrn, pMode, RR_Rotate_0); +#else + setModeRandR11(pScrn, pMode, false, false, 0, 0); +#endif + TRACE_LOG("returning %s\n", rc ? "TRUE" : "FALSE"); + return rc; +} + +static void VBOXAdjustFrame(ScrnInfoPtr pScrn, int x, int y) +{ RT_NOREF(pScrn, x, y); } + +static void VBOXFreeScreen(ScrnInfoPtr pScrn) +{ + /* Destroy the VGA hardware record */ + vgaHWFreeHWRec(pScrn); + /* And our private record */ + free(pScrn->driverPrivate); + pScrn->driverPrivate = NULL; +} + +static Bool +VBOXMapVidMem(ScrnInfoPtr pScrn) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + Bool rc = TRUE; + + TRACE_ENTRY(); + if (!pVBox->base) + { +#ifdef PCIACCESS + (void) pci_device_map_range(pVBox->pciInfo, + pScrn->memPhysBase, + pScrn->videoRam * 1024, + PCI_DEV_MAP_FLAG_WRITABLE, + & pVBox->base); +#else + pVBox->base = xf86MapPciMem(pScrn->scrnIndex, + VIDMEM_FRAMEBUFFER, + pVBox->pciTag, pScrn->memPhysBase, + (unsigned) pScrn->videoRam * 1024); +#endif + if (!pVBox->base) + rc = FALSE; + } + TRACE_LOG("returning %s\n", rc ? "TRUE" : "FALSE"); + return rc; +} + +static void +VBOXUnmapVidMem(ScrnInfoPtr pScrn) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + + TRACE_ENTRY(); + if (pVBox->base == NULL) + return; + +#ifdef PCIACCESS + (void) pci_device_unmap_range(pVBox->pciInfo, + pVBox->base, + pScrn->videoRam * 1024); +#else + xf86UnMapVidMem(pScrn->scrnIndex, pVBox->base, + (unsigned) pScrn->videoRam * 1024); +#endif + pVBox->base = NULL; + TRACE_EXIT(); +} + +#ifndef VBOXVIDEO_13 +static Bool +VBOXSaveScreen(ScreenPtr pScreen, int mode) +{ + RT_NOREF(pScreen, mode); + return TRUE; +} +#endif + +void +VBOXSaveMode(ScrnInfoPtr pScrn) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + vgaRegPtr vgaReg; + + TRACE_ENTRY(); + vgaReg = &VGAHWPTR(pScrn)->SavedReg; + vgaHWSave(pScrn, vgaReg, VGA_SR_ALL); + pVBox->fSavedVBEMode = VBoxVideoGetModeRegisters(&pVBox->cSavedWidth, + &pVBox->cSavedHeight, + &pVBox->cSavedPitch, + &pVBox->cSavedBPP, + &pVBox->fSavedFlags); +} + +void +VBOXRestoreMode(ScrnInfoPtr pScrn) +{ + VBOXPtr pVBox = VBOXGetRec(pScrn); + vgaRegPtr vgaReg; + + TRACE_ENTRY(); + vgaReg = &VGAHWPTR(pScrn)->SavedReg; + vgaHWRestore(pScrn, vgaReg, VGA_SR_ALL); + if (pVBox->fSavedVBEMode) + VBoxVideoSetModeRegisters(pVBox->cSavedWidth, pVBox->cSavedHeight, + pVBox->cSavedPitch, pVBox->cSavedBPP, + pVBox->fSavedFlags, 0, 0); + else + VBoxVideoDisableVBE(); +} + +#ifndef VBOXVIDEO_13 +static void +VBOXDisplayPowerManagementSet(ScrnInfoPtr pScrn, int mode, int flags) +{ + RT_NOREF(pScrn, mode, flags); +} +#endif diff --git a/src/VBox/Additions/x11/vboxvideo/vboxvideo.h b/src/VBox/Additions/x11/vboxvideo/vboxvideo.h new file mode 100644 index 00000000..fcc0d983 --- /dev/null +++ b/src/VBox/Additions/x11/vboxvideo/vboxvideo.h @@ -0,0 +1,240 @@ +/* $Id: vboxvideo.h $ */ +/** @file + * VirtualBox X11 Additions graphics driver + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * This file is based on the X11 VESA driver: + * + * Copyright (c) 2000 by Conectiva S.A. (http://www.conectiva.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS 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 COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 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 Conectiva Linux shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization from + * Conectiva Linux. + * + * Authors: Paulo César Pereira de Andrade <pcpa@conectiva.com.br> + * Michael Thayer <michael.thayer@oracle.com> + */ + +#ifndef GA_INCLUDED_SRC_x11_vboxvideo_vboxvideo_h +#define GA_INCLUDED_SRC_x11_vboxvideo_vboxvideo_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <VBoxVideoGuest.h> +#include <VBoxVideo.h> +#include "version-generated.h" + +#define VBOX_VENDORID 0x80EE +#define VBOX_DEVICEID 0xBEEF + +#ifndef VBVA_SCREEN_F_BLANK +# define VBVA_SCREEN_F_BLANK 0x0004 +#endif + +#include <VBoxVideoVBE.h> + +#include "xf86.h" +#include "xf86str.h" +#include "xf86Cursor.h" + +#ifdef DEBUG + +#define TRACE_ENTRY() do { xf86ErrorF("%s: entering\n", __func__); } while(0) +#define TRACE_EXIT() do { xf86ErrorF("%s: leaving\n", __func__); } while(0) +#define TRACE_LINE() \ + do { xf86ErrorF("%s: line\n", __func__, __LINE__); } while(0) +#define TRACE_LOG(...) \ +do { \ + xf86ErrorF("%s: ", __func__); \ + xf86ErrorF(__VA_ARGS__); \ +} while(0) + +#else /* !DEBUG */ + +#define TRACE_ENTRY() do { } while (0) +#define TRACE_EXIT() do { } while (0) +#define TRACE_LOG(...) do { } while (0) + +#endif /* !DEBUG */ + +#define VBOX_VERSION VBOX_VERSION_MAJOR * 10000 \ + + VBOX_VERSION_MINOR * 100 +#define VBOX_NAME "VBoxVideo" +#define VBOX_DRIVER_NAME "vboxvideo" + +#define VBOX_VIDEO_MAJOR VBOX_VERSION_MAJOR +#define VBOX_VIDEO_MINOR VBOX_VERSION_MINOR + +#define VBOX_VIDEO_MIN_SIZE 64 +#define VBOX_VIDEO_MAX_VIRTUAL (INT16_MAX - 1) + +#define VBOXPTR(p) ((VBOXPtr)((p)->driverPrivate)) + +/** Helper to work round different ways of getting the root window in different + * server versions. */ +#if defined(XORG_VERSION_CURRENT) && XORG_VERSION_CURRENT < 70000000 \ + && XORG_VERSION_CURRENT >= 10900000 +# define ROOT_WINDOW(pScrn) screenInfo.screens[(pScrn)->scrnIndex]->root +#else +# define ROOT_WINDOW(pScrn) WindowTable[(pScrn)->scrnIndex] +#endif + +/** ChangeWindowProperty for X.Org Server 1.19 and later */ +#if defined(XORG_VERSION_CURRENT) && XORG_VERSION_CURRENT < 70000000 \ + && XORG_VERSION_CURRENT >= 11900000 +# define ChangeWindowProperty(pWin, property, type, format, mode, \ + len, value, sendevent) \ + dixChangeWindowProperty(serverClient, pWin, property, type, format, \ + mode, len, value, sendevent) +#endif + +/** Structure containing all virtual monitor-specific information. */ +struct VBoxScreen +{ + /** Position information for each virtual screen for the purposes of + * sending dirty rectangle information to the right one. */ + RTRECT2 aScreenLocation; + /** Is this CRTC enabled or in DPMS off state? */ + Bool fPowerOn; +#ifdef VBOXVIDEO_13 + /** The virtual crtcs. */ + struct _xf86Crtc *paCrtcs; + /** The virtual outputs, logically not distinct from crtcs. */ + struct _xf86Output *paOutputs; +#endif + /** Offsets of VBVA buffers in video RAM */ + uint32_t aoffVBVABuffer; + /** Context information about the VBVA buffers for each screen */ + struct VBVABUFFERCONTEXT aVbvaCtx; + /** The current preferred resolution for the screen */ + RTRECTSIZE aPreferredSize; + /** The current preferred location for the screen. */ + RTPOINT aPreferredLocation; + /** Has this screen been enabled by the host? */ + Bool afConnected; + /** Does this screen have a preferred location? */ + Bool afHaveLocation; +}; + +typedef struct VBOXRec +{ + EntityInfoPtr pEnt; +#ifdef PCIACCESS + struct pci_device *pciInfo; +#else + pciVideoPtr pciInfo; + PCITAG pciTag; +#endif + void *base; + /** The amount of VRAM available for use as a framebuffer */ + unsigned long cbFBMax; + /** The size of the framebuffer and the VBVA buffers at the end of it. */ + unsigned long cbView; + /** Whether the pre-X-server mode was a VBE mode */ + Bool fSavedVBEMode; + /** Paramters of the saved pre-X-server VBE mode, invalid if there is none + */ + uint16_t cSavedWidth, cSavedHeight, cSavedPitch, cSavedBPP, fSavedFlags; + CloseScreenProcPtr CloseScreen; + /** Default X server procedure for enabling and disabling framebuffer access */ + xf86EnableDisableFBAccessProc *EnableDisableFBAccess; + OptionInfoPtr Options; + /** @todo we never actually free this */ + xf86CursorInfoPtr pCurs; + /** Do we currently want to use the host cursor? */ + Bool fUseHardwareCursor; + /** Number of screens attached */ + uint32_t cScreens; + /** Information about each virtual screen. */ + struct VBoxScreen *pScreens; + /** Can we get mode hint and cursor integration information from HGSMI? */ + Bool fHaveHGSMIModeHints; + /** Does the host support the screen blanking flag? */ + Bool fHostHasScreenBlankingFlag; + /** Array of structures for receiving mode hints. */ + VBVAMODEHINT *paVBVAModeHints; +#ifdef VBOXVIDEO_13 +# ifdef RT_OS_LINUX + /** Input device file descriptor for getting ACPI hot-plug events. */ + int fdACPIDevices; + /** Input handler handle for ACPI hot-plug listener. */ + void *hACPIEventHandler; +# endif +#endif + /** HGSMI guest heap context */ + HGSMIGUESTCOMMANDCONTEXT guestCtx; + /** Unrestricted horizontal resolution flag. */ + Bool fAnyX; +} VBOXRec, *VBOXPtr; + +#define VBOXGetRec(pScrn) ((VBOXPtr)(pScrn)->driverPrivate) + +/* setmode.c */ + +/** Structure describing the virtual frame buffer. It starts at the beginning + * of the video RAM. */ +struct vbvxFrameBuffer { + /** X offset of first screen in frame buffer. */ + int x0; + /** Y offset of first screen in frame buffer. */ + int y0; + /** Frame buffer virtual width. */ + unsigned cWidth; + /** Frame buffer virtual height. */ + unsigned cHeight; + /** Bits per pixel. */ + unsigned cBPP; +}; + +extern void vbvxClearVRAM(ScrnInfoPtr pScrn, size_t cbOldSize, size_t cbNewSize); +extern void vbvxSetMode(ScrnInfoPtr pScrn, unsigned cDisplay, unsigned cWidth, unsigned cHeight, int x, int y, Bool fEnabled, + Bool fConnected, struct vbvxFrameBuffer *pFrameBuffer); +extern void vbvxSetSolarisMouseRange(int width, int height); + +/* pointer.h */ +extern Bool vbvxCursorInit(ScreenPtr pScreen); +extern void vbvxCursorTerm(VBOXPtr pVBox); + +/* vbva.c */ +extern void vbvxHandleDirtyRect(ScrnInfoPtr pScrn, int iRects, BoxPtr aRects); +extern void vbvxSetUpHGSMIHeapInGuest(VBOXPtr pVBox, uint32_t cbVRAM); +extern Bool vboxEnableVbva(ScrnInfoPtr pScrn); +extern void vboxDisableVbva(ScrnInfoPtr pScrn); + +/* getmode.c */ +extern void vboxAddModes(ScrnInfoPtr pScrn); +extern void VBoxInitialiseSizeHints(ScrnInfoPtr pScrn); +extern void vbvxReadSizesAndCursorIntegrationFromProperties(ScrnInfoPtr pScrn, Bool *pfNeedUpdate); +extern void vbvxReadSizesAndCursorIntegrationFromHGSMI(ScrnInfoPtr pScrn, Bool *pfNeedUpdate); +extern void vbvxSetUpLinuxACPI(ScreenPtr pScreen); +extern void vbvxCleanUpLinuxACPI(ScreenPtr pScreen); + +/* EDID generation */ +#ifdef VBOXVIDEO_13 +extern Bool VBOXEDIDSet(struct _xf86Output *output, DisplayModePtr pmode); +#endif + +#endif /* !GA_INCLUDED_SRC_x11_vboxvideo_vboxvideo_h */ + diff --git a/src/VBox/Additions/x11/vboxvideo/vbva.c b/src/VBox/Additions/x11/vboxvideo/vbva.c new file mode 100644 index 00000000..c478e399 --- /dev/null +++ b/src/VBox/Additions/x11/vboxvideo/vbva.c @@ -0,0 +1,255 @@ +/* $Id: vbva.c $ */ +/** @file + * VirtualBox X11 Additions graphics driver 2D acceleration functions + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if defined(IN_XF86_MODULE) && !defined(NO_ANSIC) +# include "xf86_ansic.h" +#endif +#include "compiler.h" + +#include "vboxvideo.h" + +#ifdef XORG_7X +# include <stdlib.h> +# include <string.h> +#endif + +/************************************************************************** +* Main functions * +**************************************************************************/ + +/** + * Callback function called by the X server to tell us about dirty + * rectangles in the video buffer. + * + * @param pScrn pointer to the information structure for the current + * screen + * @param iRects Number of dirty rectangles to update + * @param aRects Array of structures containing the coordinates of the + * rectangles + */ +void vbvxHandleDirtyRect(ScrnInfoPtr pScrn, int iRects, BoxPtr aRects) +{ + VBVACMDHDR cmdHdr; + VBOXPtr pVBox; + int i; + unsigned j; + + pVBox = pScrn->driverPrivate; + if (!pScrn->vtSema) + return; + + for (j = 0; j < pVBox->cScreens; ++j) + { + /* Just continue quietly if VBVA is not currently active. */ + struct VBVABUFFER *pVBVA = pVBox->pScreens[j].aVbvaCtx.pVBVA; + if ( !pVBVA + || !(pVBVA->hostFlags.u32HostEvents & VBVA_F_MODE_ENABLED)) + continue; + for (i = 0; i < iRects; ++i) + { + if ( aRects[i].x1 > pVBox->pScreens[j].aScreenLocation.x + + pVBox->pScreens[j].aScreenLocation.cx + || aRects[i].y1 > pVBox->pScreens[j].aScreenLocation.y + + pVBox->pScreens[j].aScreenLocation.cy + || aRects[i].x2 < pVBox->pScreens[j].aScreenLocation.x + || aRects[i].y2 < pVBox->pScreens[j].aScreenLocation.y) + continue; + cmdHdr.x = (int16_t)aRects[i].x1 - pVBox->pScreens[0].aScreenLocation.x; + cmdHdr.y = (int16_t)aRects[i].y1 - pVBox->pScreens[0].aScreenLocation.y; + cmdHdr.w = (uint16_t)(aRects[i].x2 - aRects[i].x1); + cmdHdr.h = (uint16_t)(aRects[i].y2 - aRects[i].y1); + +#if 0 + TRACE_LOG("display=%u, x=%d, y=%d, w=%d, h=%d\n", + j, cmdHdr.x, cmdHdr.y, cmdHdr.w, cmdHdr.h); +#endif + + if (VBoxVBVABufferBeginUpdate(&pVBox->pScreens[j].aVbvaCtx, + &pVBox->guestCtx)) + { + VBoxVBVAWrite(&pVBox->pScreens[j].aVbvaCtx, &pVBox->guestCtx, &cmdHdr, + sizeof(cmdHdr)); + VBoxVBVABufferEndUpdate(&pVBox->pScreens[j].aVbvaCtx); + } + } + } +} + +static DECLCALLBACK(void *) hgsmiEnvAlloc(void *pvEnv, HGSMISIZE cb) +{ + RT_NOREF(pvEnv); + return calloc(1, cb); +} + +static DECLCALLBACK(void) hgsmiEnvFree(void *pvEnv, void *pv) +{ + RT_NOREF(pvEnv); + free(pv); +} + +static HGSMIENV g_hgsmiEnv = +{ + NULL, + hgsmiEnvAlloc, + hgsmiEnvFree +}; + +/** + * Calculate the location in video RAM of and initialise the heap for guest to + * host messages. + */ +void vbvxSetUpHGSMIHeapInGuest(VBOXPtr pVBox, uint32_t cbVRAM) +{ + int rc; + uint32_t offVRAMBaseMapping, offGuestHeapMemory, cbGuestHeapMemory; + void *pvGuestHeapMemory; + + VBoxHGSMIGetBaseMappingInfo(cbVRAM, &offVRAMBaseMapping, NULL, &offGuestHeapMemory, &cbGuestHeapMemory, NULL); + pvGuestHeapMemory = ((uint8_t *)pVBox->base) + offVRAMBaseMapping + offGuestHeapMemory; + rc = VBoxHGSMISetupGuestContext(&pVBox->guestCtx, pvGuestHeapMemory, cbGuestHeapMemory, + offVRAMBaseMapping + offGuestHeapMemory, &g_hgsmiEnv); + AssertMsg(RT_SUCCESS(rc), ("Failed to set up the guest-to-host message buffer heap, rc=%d\n", rc)); + pVBox->cbView = offVRAMBaseMapping; +} + +/** Callback to fill in the view structures */ +static DECLCALLBACK(int) vboxFillViewInfo(void *pvVBox, struct VBVAINFOVIEW *pViews, uint32_t cViews) +{ + VBOXPtr pVBox = (VBOXPtr)pvVBox; + unsigned i; + for (i = 0; i < cViews; ++i) + { + pViews[i].u32ViewIndex = i; + pViews[i].u32ViewOffset = 0; + pViews[i].u32ViewSize = pVBox->cbView; + pViews[i].u32MaxScreenSize = pVBox->cbFBMax; + } + return VINF_SUCCESS; +} + +/** + * Initialise VirtualBox's accelerated video extensions. + * + * @returns TRUE on success, FALSE on failure + */ +static Bool vboxSetupVRAMVbva(VBOXPtr pVBox) +{ + int rc = VINF_SUCCESS; + unsigned i; + + pVBox->cbFBMax = pVBox->cbView; + for (i = 0; i < pVBox->cScreens; ++i) + { + pVBox->cbFBMax -= VBVA_MIN_BUFFER_SIZE; + pVBox->pScreens[i].aoffVBVABuffer = pVBox->cbFBMax; + TRACE_LOG("VBVA buffer offset for screen %u: 0x%lx\n", i, + (unsigned long) pVBox->cbFBMax); + VBoxVBVASetupBufferContext(&pVBox->pScreens[i].aVbvaCtx, + pVBox->pScreens[i].aoffVBVABuffer, + VBVA_MIN_BUFFER_SIZE); + } + TRACE_LOG("Maximum framebuffer size: %lu (0x%lx)\n", + (unsigned long) pVBox->cbFBMax, + (unsigned long) pVBox->cbFBMax); + rc = VBoxHGSMISendViewInfo(&pVBox->guestCtx, pVBox->cScreens, + vboxFillViewInfo, (void *)pVBox); + AssertMsg(RT_SUCCESS(rc), ("Failed to send the view information to the host, rc=%d\n", rc)); + return TRUE; +} + +static Bool haveHGSMIModeHintAndCursorReportingInterface(VBOXPtr pVBox) +{ + uint32_t fModeHintReporting, fCursorReporting; + + return RT_SUCCESS(VBoxQueryConfHGSMI(&pVBox->guestCtx, VBOX_VBVA_CONF32_MODE_HINT_REPORTING, &fModeHintReporting)) + && RT_SUCCESS(VBoxQueryConfHGSMI(&pVBox->guestCtx, VBOX_VBVA_CONF32_GUEST_CURSOR_REPORTING, &fCursorReporting)) + && fModeHintReporting == VINF_SUCCESS + && fCursorReporting == VINF_SUCCESS; +} + +static Bool hostHasScreenBlankingFlag(VBOXPtr pVBox) +{ + uint32_t fScreenFlags; + + return RT_SUCCESS(VBoxQueryConfHGSMI(&pVBox->guestCtx, VBOX_VBVA_CONF32_SCREEN_FLAGS, &fScreenFlags)) + && fScreenFlags & VBVA_SCREEN_F_BLANK; +} + +/** + * Inform VBox that we will supply it with dirty rectangle information + * and install the dirty rectangle handler. + * + * @returns TRUE for success, FALSE for failure + * @param pScrn Pointer to a structure describing the X screen in use + */ +Bool +vboxEnableVbva(ScrnInfoPtr pScrn) +{ + Bool rc = TRUE; + unsigned i; + VBOXPtr pVBox = pScrn->driverPrivate; + + TRACE_ENTRY(); + if (!vboxSetupVRAMVbva(pVBox)) + return FALSE; + for (i = 0; i < pVBox->cScreens; ++i) + { + struct VBVABUFFER *pVBVA; + + pVBVA = (struct VBVABUFFER *) ( ((uint8_t *)pVBox->base) + + pVBox->pScreens[i].aoffVBVABuffer); + if (!VBoxVBVAEnable(&pVBox->pScreens[i].aVbvaCtx, &pVBox->guestCtx, + pVBVA, i)) + rc = FALSE; + } + AssertMsg(rc, ("Failed to enable screen update reporting for at least one virtual monitor.\n")); + pVBox->fHaveHGSMIModeHints = haveHGSMIModeHintAndCursorReportingInterface(pVBox); + pVBox->fHostHasScreenBlankingFlag = hostHasScreenBlankingFlag(pVBox); + return rc; +} + +/** + * Inform VBox that we will stop supplying it with dirty rectangle + * information. This function is intended to be called when an X + * virtual terminal is disabled, or the X server is terminated. + * + * @returns TRUE for success, FALSE for failure + * @param pScrn Pointer to a structure describing the X screen in use + */ +void +vboxDisableVbva(ScrnInfoPtr pScrn) +{ + unsigned i; + VBOXPtr pVBox = pScrn->driverPrivate; + + TRACE_ENTRY(); + for (i = 0; i < pVBox->cScreens; ++i) + VBoxVBVADisable(&pVBox->pScreens[i].aVbvaCtx, &pVBox->guestCtx, i); +} |