March 8, 2004
Based on feedback from Tom Vera
October 7, 2004
I've patched Xvnc 3.3.7 (the Debian 3.3.7-6 package)
to support the X Resize and Rotation extension (RANDR).
The modified X server understands the standard RANDR extension
protocol, and supports all four rotation possibilities, but not
reflection (those this would not be difficult to add). The xrandr
utility (distributed with XFree 4.4.0, not sure about other versions)
can be used to query and set the current screen rotation.
I use it on my Toshiba Portege 3505 (a convertible laptop/tablet
model) with a standard X server running xvncviewer in fullscreen
mode to allow rotations into different screen orientations.
There is a virtual keyboard program (xvkbd) available separately.
The modified Xvnc source code can be downloaded
here.
A Linux executable (compiled against libc 2.3.2) is available
here.
I started this project by writing a Perl script (available
here) that sits between a client and a
server and rotates the server's output by 180 degrees. It's slow, but
since it reads and decodes both the client and server VNC protocol, it
could be used for other Perl-based VNC applications.
The changes are not difficult to understand. Basically, I ripped the
generic RANDR code from XFree 4.4.0 and backported it (easily - few
changes were required) into the Xvnc server (based on XFree 3.3.2).
All the VNC-specific changes are confined to the hw/vnc
directory. When the VNC X server initializes, it creates a
framebuffer in memory that all the X operations write into. The
pixels are pulled out of this memory buffer and transmitted to the
clients using the VNC protocol. This is the way it has always worked.
The modified server creates a square in-memory framebuffer and
initializes the X code with square dimensions. For example, if you
request a 1024x768 display, a 1024x1024 framebuffer is created. The X
operations don't require any modifications to write into this larger
framebuffer. Only the parts actually visable are transmitted to the
clients. Only a few elements in the X screen structure are altered to
indicate the actual width/height of the screen, which are only used in
a few places by the X server. To rotate, we leave the in-memory
framebuffer unaltered (so the X part of the code never does anything
special), and changes the VNC transmission code to pull the pixels out
of the framebuffer in the correct order for the current rotation.
The server is started as normal:
And I can connect to it as normal:
I've added options to my fvwm2 configuration to allow menu-driven orientation changes:
...and in my MenuFvwmRoot section I add...
The Restart command is included on all the orientation
changes in case we've switched between landscape and portrait modes;
then fvwm needs to reinitialize to get the new screen geometry. If,
like me, you use fvwm's virtual desktop feature, you can take
advantage of the 4:3 aspect ratio of most display hardware to create a
square virtual desktop whose dimensions remain constant during
orientation changes:
In my case, this creates a 3072x3072 virtual desktop (3072 = 1024x3 =
768x4) partitioned into either a 3x4 1024x768 geometry or a 4x3
768x1024 geometry. Since the virtual desktop size is constant,
windows can never disappear from the desktop after an orientation
change. For this to work, you need to preprocess this
statement with M4, see the FvwmM4 manual page for details.
Availability
How it works
Example
vncserver -geometry 1024x768 :1
xvncviewer -fullscreen localhost:1
AddToFunc PrimaryLandscape
+ I Exec xrandr -o normal
+ I Restart
AddToFunc SecondaryLandscape
+ I Exec xrandr -o inverted
+ I Restart
AddToFunc PrimaryPortrait
+ I Exec xrandr -o right
+ I Restart
AddToFunc SecondaryPortrait
+ I Exec xrandr -o left
+ I Restart
DestroyMenu MenuOrientation
AddToMenu MenuOrientation "$[gt.Screen Orientation]" Title
+ "Primary Landscape" Function PrimaryLandscape
+ "Secondary Landscape" Function SecondaryLandscape
+ "Primary Portrait" Function PrimaryPortrait
+ "Secondary Portrait" Function SecondaryPortrait
+ "&O. Orientation" Popup MenuOrientation
DeskTopSize ifelse(eval(WIDTH>HEIGHT),1,3x4,4x3)