kopia lustrzana https://github.com/browsh-org/browsh
Actual usable zooming and panning! Still needs work though.
rodzic
932a4b3da9
commit
0afecac2dc
146
stdin_forward.go
146
stdin_forward.go
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
100
xzoom/xzoom.h
100
xzoom/xzoom.h
|
@ -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();
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue