Add display harness hack
This commit is contained in:
parent
50ce403c0e
commit
e774a8a13c
85
apps/qemu-display-hack.c
Normal file
85
apps/qemu-display-hack.c
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Daemon to communicate resolution changes with QEMU over serial.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <kernel/video.h>
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
|
||||
if (system("qemu-fwcfg -q opt/org.toaruos.displayharness") != 0) {
|
||||
fprintf(stderr, "%s: display harness not enabled\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Display harness is running.\n");
|
||||
|
||||
int fd = open("/dev/fb0", O_RDONLY);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "%s: failed to open framebuffer: %s\n", argv[0], strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Framebuffer opened.\n");
|
||||
|
||||
struct vid_size s;
|
||||
|
||||
FILE * f = fopen("/dev/ttyS0","r+");
|
||||
if (!f) {
|
||||
fprintf(stderr, "%s: failed to open serial: %s\n", argv[0], strerror(errno));
|
||||
}
|
||||
|
||||
fprintf(stderr, "Serial opened.\n");
|
||||
|
||||
if (!fork()) {
|
||||
|
||||
while (!feof(f)) {
|
||||
char data[128];
|
||||
fgets(data, 128, f);
|
||||
|
||||
char * linefeed = strstr(data,"\n");
|
||||
if (linefeed) { *linefeed = '\0'; }
|
||||
|
||||
fprintf(stderr, "Received a line from serial: [%s]\n", data);
|
||||
|
||||
char * width;
|
||||
char * height;
|
||||
|
||||
width = strstr(data, " ");
|
||||
if (width) {
|
||||
*width = '\0';
|
||||
width++;
|
||||
} else {
|
||||
continue; /* bad line */
|
||||
}
|
||||
|
||||
height = strstr(width, " ");
|
||||
if (height) {
|
||||
*height = '\0';
|
||||
height++;
|
||||
} else {
|
||||
continue; /* bad line */
|
||||
}
|
||||
|
||||
s.width = atoi(width);
|
||||
s.height = atoi(height);
|
||||
|
||||
fprintf(stderr, "Setting resolution to %d x %d\n", (int)s.width, (int)s.height);
|
||||
|
||||
ioctl(fd, IO_VID_SET, &s);
|
||||
fprintf(f, "X");
|
||||
fflush(f);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Disconnecting daemon.\n");
|
||||
|
||||
return 0;
|
||||
}
|
116
util/qemu-harness.py
Executable file
116
util/qemu-harness.py
Executable file
@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Harness for running QEMU and communicating window sizes through serial.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import asyncio
|
||||
import time
|
||||
import sys
|
||||
|
||||
from Xlib.display import Display
|
||||
from Xlib.protocol.event import KeyPress, KeyRelease
|
||||
from Xlib.XK import string_to_keysym
|
||||
import Xlib
|
||||
|
||||
qemu_bin = 'qemu-system-i386'
|
||||
|
||||
qemu = subprocess.Popen([
|
||||
qemu_bin,
|
||||
'-enable-kvm',
|
||||
'-cdrom','image.iso',
|
||||
# 1GB of RAM
|
||||
'-m','1G',
|
||||
# Enable audio
|
||||
'-soundhw','ac97,pcspk',
|
||||
# The GTK interface does not play well, force SDL
|
||||
'-display', 'sdl',
|
||||
# Redirect serial to TCP server we can connect to
|
||||
'-serial','tcp::4444,server,nowait',
|
||||
# Add a VGA card with 32mb of video RAM
|
||||
'-device', 'VGA,id=video0,vgamem_mb=32',
|
||||
# Set the fwcfg flag so our userspace app recognizes us
|
||||
'-fw_cfg','name=opt/org.toaruos.displayharness,string=1'
|
||||
])
|
||||
|
||||
# Give QEMU some time to start up and create a window.
|
||||
time.sleep(1)
|
||||
|
||||
# Find the QEMU window...
|
||||
def findQEMU(window):
|
||||
try:
|
||||
x = window.get_wm_name()
|
||||
if 'QEMU' in x:
|
||||
return window
|
||||
except:
|
||||
pass
|
||||
children = window.query_tree().children
|
||||
for w in children:
|
||||
x = findQEMU(w)
|
||||
if x: return x
|
||||
return None
|
||||
|
||||
display = Display()
|
||||
root = display.screen().root
|
||||
qemu_win = findQEMU(root)
|
||||
|
||||
def send_key(key, state, up=False):
|
||||
"""Send a key press or release to the QEMU window."""
|
||||
time.sleep(0.1)
|
||||
t = KeyPress
|
||||
if up:
|
||||
t = KeyRelease
|
||||
|
||||
sym = string_to_keysym(key)
|
||||
ke = t(
|
||||
time=int(time.time()),
|
||||
root=display.screen().root,
|
||||
window=qemu_win,
|
||||
same_screen=0,
|
||||
child=Xlib.X.NONE,
|
||||
root_x = 0, root_y = 0, event_x = 0, event_y = 0,
|
||||
state = 0xc,
|
||||
detail = display.keysym_to_keycode(sym)
|
||||
)
|
||||
qemu_win.send_event(ke)
|
||||
display.flush()
|
||||
|
||||
class Client(asyncio.Protocol):
|
||||
|
||||
def connection_made(self, transport):
|
||||
asyncio.ensure_future(heartbeat(transport))
|
||||
|
||||
def data_received(self, data):
|
||||
if 'X' in data.decode('utf-8'):
|
||||
# Send Ctrl-Alt-u
|
||||
send_key('Control_L',0x00)
|
||||
send_key('Alt_L',0x04)
|
||||
send_key('u',0x0c)
|
||||
send_key('u',0x0c,True)
|
||||
send_key('Alt_L',0x0c,True)
|
||||
send_key('Control_L',0x04,True)
|
||||
|
||||
async def heartbeat(transport):
|
||||
"""Heartbeat process checks window size every second and sends update signal."""
|
||||
w = 0
|
||||
h = 0
|
||||
while 1:
|
||||
await asyncio.sleep(1)
|
||||
try:
|
||||
g = qemu_win.get_geometry()
|
||||
except Xlib.error.BadDrawable:
|
||||
print("QEMU window is gone, exiting.")
|
||||
asyncio.get_event_loop().call_soon(sys.exit, 0)
|
||||
return
|
||||
if g.width != w or g.height != h:
|
||||
print("Changed:",g.width,g.height)
|
||||
transport.write(("geometry-changed %d %d\n" % (g.width,g.height)).encode('utf-8'))
|
||||
w = g.width
|
||||
h = g.height
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
coro = loop.create_connection(Client,'127.0.0.1',4444)
|
||||
asyncio.ensure_future(coro)
|
||||
loop.run_forever()
|
||||
loop.close()
|
||||
|
Loading…
Reference in New Issue
Block a user