Actual usable zooming and panning! Still needs work though.

pull/16/head
Thomas Buckley-Houston 2016-05-20 14:17:58 +09:00
rodzic 932a4b3da9
commit 0afecac2dc
3 zmienionych plików z 111 dodań i 156 usunięć

Wyświetl plik

@ -43,6 +43,10 @@ var zoomLevel float32
var viewport map[string] float32
func initialise() {
tErr := os.Truncate(logfile, 0)
if tErr != nil {
panic(tErr)
}
log("Starting...")
calculateHipDimensions()
zoomLevel = 1
@ -79,16 +83,15 @@ func min(a float32, b float32) float32 {
}
func log(msg string) {
msg = msg + "\n"
f, err := os.OpenFile(logfile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
panic(err)
f, oErr := os.OpenFile(logfile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if oErr != nil {
panic(oErr)
}
defer f.Close()
if _, err = f.WriteString(msg); err != nil {
panic(err)
msg = msg + "\n"
if _, wErr := f.WriteString(msg); wErr != nil {
panic(wErr)
}
}
@ -119,6 +122,11 @@ func ctrlPressed() bool {
return curev.Mod&termbox.ModCtrl != 0
}
// Whether the mouse is moving
func mouseMotion() bool {
return curev.Mod&termbox.ModMotion != 0
}
// Convert Termbox symbols to xdotool arguments
func mouseButtonStr(k termbox.Key) []string {
switch k {
@ -142,8 +150,10 @@ func mouseButtonStr(k termbox.Key) []string {
return []string{"click", "4"}
case termbox.MouseWheelDown:
if ctrlPressed() {
C.magx--
C.magy--
if C.magx > 1 {
C.magx--
C.magy--
}
return []string{"noop"}
}
return []string{"click", "5"}
@ -169,102 +179,68 @@ func modStr(m termbox.Modifier) string {
return strings.Join(out, " ")
}
var panNeedsSetup bool
var panCachedXOffset float32
var panCachedYOffset float32
func mouseEvent() {
setCurrentDesktopCoords()
// Always move the mouse first. This is because we're not constantly updating the mouse position,
// *unless* a drag event is happening. This saves bandwidth. Also, mouse movement isn't supported
// on all terminals.
xdotool("mousemove", fmt.Sprintf("%d", roundedDesktopX), fmt.Sprintf("%d", roundedDesktopY))
log(
fmt.Sprintf(
"EventMouse: x: %d, y: %d, b: %s, mod: %s",
curev.MouseX, curev.MouseY, mouseButtonStr(curev.Key), modStr(curev.Mod)))
// CTRL allows the user to drag the mouse to pan and zoom the desktop.
if !ctrlPressed() {
xdotool("keyup", "alt")
}
// Always move the mouse first. This is because we're not constantly updating the mouse position,
// *unless* a drag event is happening. This saves bandwidth. Also, mouse movement isn't supported
// on all terminals.
setCurrentDesktopCoords()
xdotool("mousemove", fmt.Sprintf("%d", roundedDesktopX), fmt.Sprintf("%d", roundedDesktopY))
// Send a button press to X. Note that the "Motion" modifier is sent when the user is doing
// a drag event and thus mouse reporting will be constantly streamed.
if !strings.Contains(modStr(curev.Mod), "Motion") {
xdotool(mouseButtonStr(curev.Key)...)
if ctrlPressed() && mouseMotion() && lastMouseButton == "1" {
C.pan = 1
if panNeedsSetup == true {
panCachedXOffset = float32(C.xgrab)
panCachedYOffset = float32(C.ygrab)
}
panNeedsSetup = false
} else {
panNeedsSetup = true
C.pan = 0
if !ctrlPressed() {
xdotool(mouseButtonStr(curev.Key)...)
}
}
}
// Convert terminal coords into desktop coords
func setCurrentDesktopCoords() {
var xOffset float32
var yOffset float32
hipWidthFloat := float32(hipWidth)
hipHeightFloat := float32(hipHeight)
eventX := float32(curev.MouseX)
eventY := float32(curev.MouseY)
desktopXFloat = (eventX * (viewport["xSize"] / hipWidthFloat)) + viewport["xOffset"]
desktopYFloat = (eventY * (viewport["ySize"] / hipHeightFloat)) + viewport["yOffset"]
width := float32(C.width[C.SRC])
height := float32(C.height[C.SRC])
if C.pan == 1 {
// When panning starts we want to do it all within the same viewport.
// Without the caching here then the viewport would change for each
// mouse movement and panning becomes overly sensitive.
xOffset = panCachedXOffset
yOffset = panCachedYOffset
} else {
xOffset = float32(C.xgrab)
yOffset = float32(C.ygrab)
}
desktopXFloat = (eventX * (width / hipWidthFloat)) + xOffset
desktopYFloat = (eventY * (height / hipHeightFloat)) + yOffset
log(
fmt.Sprintf(
"setCurrentDesktopCoords: tw: %d, th: %d, dx: %d, dy: %d",
hipHeightFloat, hipWidthFloat, desktopXFloat, desktopYFloat))
"setCurrentDesktopCoords: tw: %d, th: %d, dx: %d, dy: %d, mag: %d",
hipHeightFloat, hipWidthFloat, desktopXFloat, desktopYFloat, C.magx))
roundedDesktopX = roundToInt(desktopXFloat)
roundedDesktopY = roundToInt(desktopYFloat)
}
// XFCE doesn't provide the current zoom, so *we* need to keep track of it.
// For every zoom level, the terminal coords will be mapped differently onto the X desktop.
// TODO: support custom desktop sizes.
func trackZoom(direction string) {
xdotool("keydown", "alt")
if direction == "in" {
if zoomLevel <= maxZoom {
zoomLevel += zoomFactor
} else {
return
}
} else {
if zoomLevel >= 1 {
zoomLevel -= zoomFactor
} else {
return
}
}
// Use the existing viewport to get the current coords
setCurrentDesktopCoords()
// The actual zoom
viewport["xSize"] = desktopWidth / zoomLevel
viewport["ySize"] = desktopHeight / zoomLevel
viewport["xOffset"] = desktopXFloat - (viewport["xSize"] / 2)
viewport["yOffset"] = desktopYFloat - (viewport["ySize"] / 2)
keepViewportInDesktop()
log(fmt.Sprintf("zoom: %s", zoomLevel))
log(fmt.Sprintf("viewport: %s", viewport))
}
// When zooming near the edges of the desktop it is possible that the viewport's edges overlap
// the desktop's edges. So just limit the possible movement of the viewport.
func keepViewportInDesktop() {
xLeft := viewport["xOffset"]
xRight := viewport["xOffset"] + viewport["xSize"]
yTop := viewport["yOffset"]
yBottom := viewport["yOffset"] + viewport["ySize"]
if xLeft < 0 {
viewport["xOffset"] = 0
}
if xRight > desktopWidth {
viewport["xOffset"] = desktopWidth - viewport["xSize"]
}
if yTop < 0 {
viewport["yOffset"] = 0
}
if yBottom > desktopHeight {
viewport["yOffset"] = desktopHeight - viewport["ySize"]
}
}
// Convert a keyboard event into an xdotool command
func keyEvent() {
// I've no idea why this gets picked up by the terminal, or what it refers to. But whatever, we don't
@ -310,7 +286,7 @@ func xzoomBackground(){
for {
select {
default:
C.xzoom_loop()
C.loop()
time.Sleep(40 * time.Millisecond) // 25fps
case <-stopchan:
// stop

Wyświetl plik

@ -28,18 +28,6 @@
k = magx; do *p1++ = c; while (--k > 0);
} while (--i > 0);
/* draw vertical grid */
if (gridy && magx >= 2)
{
p1 = p1_save - 1;
i = magx;
k = width[SRC];
do {
p1 += i;
*p1 ^= ~((T)0);
} while (--k > 0);
}
/* duplicate that line as needed */
if (magy > 1)
{
@ -55,15 +43,6 @@
p2 += p2step;
memcpy(p2, p1, i);
} while (--k > 0);
/* draw horizontal grid */
if (gridx && magy >= 2)
{
k = width[DST];
do {
*p2++ ^= ~((T)0);
} while (--k > 0);
}
}
} while (--j >= 0);
}

Wyświetl plik

@ -1,7 +1,7 @@
/*
This code largely comes from Itai Nahshon's xzoom, see:
http://git.r-36.net/xzoom
*/
This code largely comes from Itai Nahshon's xzoom, see:
http://git.r-36.net/xzoom
*/
#include <stdio.h>
#include <string.h>
@ -18,11 +18,9 @@
Display *dpy;
Screen *scr;
Window win;
Window win, root, child;
Status status;
Window root, child;
int rootX, rootY, winX, winY;
int pan, mouseX, mouseY, winX, winY;
unsigned int mask;
GC gc;
@ -37,21 +35,19 @@ GC gc;
#define MAGX MAG /* horizontal magnification */
#define MAGY MAG /* vertical magnification */
int xgrab, ygrab; /* where do we take the picture from */
// where do we take the picture from
int xgrab = 0;
int ygrab = 0;
int magx = MAGX;
int magy = MAGY;
int old_magx = MAGX;
int gridx = False;
int gridy = False;
int width[2] = { 0, WIDTH };
int height[2] = { 0, HEIGHT };
unsigned depth = 0;
XImage *ximage[2]; /* Ximage struct. */
XImage *ximage[2];
int created_images = False;
@ -83,40 +79,28 @@ void destroy_images(void) {
for (i = 0; i < 2; i++) {
free(ximage[i]->data);
ximage[i]->data = NULL; /* remove refrence to that address */
XDestroyImage(ximage[i]); /* and destroy image */
ximage[i]->data = NULL;
XDestroyImage(ximage[i]);
}
created_images = False;
}
/* Resize is called with the dest size.
Called when magnification changes or when
actual window size is changed */
void resize(int new_width, int new_height) {
destroy_images(); /* we can get rid of these */
void zoom() {
destroy_images();
/* find new dimensions for source */
width[SRC] = (new_width + magx - 1) / magx;
height[SRC] = (new_height + magy - 1) / magy;
width[SRC] = (width[DST] + magx - 1) / magx;
height[SRC] = (height[DST] + magy - 1) / magy;
if (width[SRC] < 1) width[SRC] = 1;
if (width[SRC] > WidthOfScreen(scr)) width[SRC] = WidthOfScreen(scr);
if (height[SRC] < 1) height[SRC] = 1;
if (height[SRC] > HeightOfScreen(scr)) height[SRC] = HeightOfScreen(scr);
/* temporary, the dest image may be larger than the
actual window */
width[DST] = magx * width[SRC];
height[DST] = magy * height[SRC];
allocate_images(); /* allocate new images */
/* remember actual window size */
if (width[DST] > new_width) width[DST] = new_width;
if (height[DST] > new_height) height[DST] = new_height;
// printf("x: %d, y: %d\n", rootX, rootY);
allocate_images();
}
void scale8(void)
@ -140,16 +124,21 @@ void scale32(void)
#undef T
}
void xzoom()
// Update the zoom window with the current state of the desktop.
// Happens at 25fps.
void update_zoom_window_with_desktop()
{
// Get a snapshot of the desktop, or a portion of the desktop
XGetSubImage(dpy, RootWindowOfScreen(scr),
xgrab, ygrab, width[SRC], height[SRC], AllPlanes,
ZPixmap, ximage[SRC], 0, 0);
// Zoom in on that snapshot
if (depth == 8) scale8();
else if (depth <= 8 * sizeof(short)) scale16();
else if (depth <= 8 * sizeof(int)) scale32();
// Put the snapshot into the xzoom window
XPutImage(dpy, win, gc, ximage[DST], 0, 0, 0, 0, width[DST], height[DST]);
XSync(dpy, 0);
}
@ -158,7 +147,6 @@ int xzoom_init() {
XSetWindowAttributes xswa;
XGCValues gcv;
char *dpyname = ":0";
int xpos = 0, ypos = 0;
atexit(destroy_images);
@ -194,30 +182,42 @@ int xzoom_init() {
GCBackground,
&gcv);
resize(width[DST], height[DST]);
zoom();
}
int xzoom_loop()
void keep_viewport_in_desktop()
{
if (old_magx != magx) {
XQueryPointer(dpy, DefaultRootWindow(dpy), &root, &child,
&mouseX, &mouseY, &winX, &winY, &mask);
xgrab = mouseX - (width[SRC] / 2);
ygrab = mouseY - (height[SRC] / 2);
resize(width[DST], height[DST]);
}
old_magx = magx;
if (xgrab < 0) xgrab = 0;
if (xgrab > WidthOfScreen(scr) - width[SRC])
xgrab = WidthOfScreen(scr) - width[SRC];
// Divide width in half to account for the double desktop width
// where the xzoom window is hidden on the right.
if (xgrab > (WidthOfScreen(scr) / 2) - width[SRC])
xgrab = (WidthOfScreen(scr) / 2) - width[SRC];
if (ygrab < 0) ygrab = 0;
if (ygrab > HeightOfScreen(scr) - height[SRC])
ygrab = HeightOfScreen(scr) - height[SRC];
xzoom();
}
void setup_viewport()
{
if (old_magx != magx || pan)
{
XQueryPointer(dpy, DefaultRootWindow(dpy), &root, &child,
&mouseX, &mouseY, &winX, &winY, &mask);
// Dividing by 2 gets us from the centre of the viewport to
// the top left corner
xgrab = mouseX - (width[SRC] / 2);
ygrab = mouseY - (height[SRC] / 2);
}
old_magx = magx;
}
void loop()
{
setup_viewport();
keep_viewport_in_desktop();
zoom();
update_zoom_window_with_desktop();
}