|
|
@@ -146,90 +146,30 @@ async function libreofficeWinId(): Promise<string> {
|
|
|
return run("xdotool", ["search", "--name", "LibreOffice Impress"]);
|
|
|
}
|
|
|
|
|
|
-// Navigate via UNO — separate script per action to avoid indentation issues
|
|
|
-function pyUnoNav(direction: "next" | "prev" | "start" | "end"): Promise<string> {
|
|
|
- // Helper snippet reused in next/prev to read position after nav
|
|
|
- const readPos = `
|
|
|
-current = 0
|
|
|
-controller = comp.getCurrentController()
|
|
|
-draw = comp.DrawPages
|
|
|
-total = draw.Count
|
|
|
-if hasattr(controller, 'getCurrentSlideIndex'):
|
|
|
- current = controller.getCurrentSlideIndex() + 1
|
|
|
- print(f"[nav] slideshow mode, index={current}", file=sys.stderr)
|
|
|
-else:
|
|
|
- page = controller.getCurrentPage()
|
|
|
- for i in range(draw.Count):
|
|
|
- if draw.getByIndex(i) == page:
|
|
|
- current = i + 1
|
|
|
- break
|
|
|
- print(f"[nav] editor mode, index={current}", file=sys.stderr)
|
|
|
-print(f"{current}/{total}")
|
|
|
-`;
|
|
|
+// Navigate via xdotool — focus the window first, then send the key.
|
|
|
+// This is more reliable than UNO for navigation since UNO has threading
|
|
|
+// issues with LibreOffice's Qt backend.
|
|
|
+async function xdotoolNav(key: string): Promise<void> {
|
|
|
+ // Search for both the editor and fullscreen slideshow window
|
|
|
+ const widRaw = await run("xdotool", [
|
|
|
+ "search", "--onlyvisible", "--name", "Impress"
|
|
|
+ ]).catch(() => "");
|
|
|
+ const wids = widRaw.split("\n").map(s => s.trim()).filter(Boolean);
|
|
|
+ if (!wids.length) {
|
|
|
+ console.warn("[xdotool] no Impress window found");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // Use the last window ID — fullscreen slideshow appears last
|
|
|
+ const wid = wids[wids.length - 1];
|
|
|
+ console.log(`[xdotool] focusing wid=${wid}, sending key=${key}`);
|
|
|
+ await run("xdotool", ["windowactivate", "--sync", wid]);
|
|
|
+ await Bun.sleep(100);
|
|
|
+ await run("xdotool", ["key", "--clearmodifiers", key]);
|
|
|
+}
|
|
|
|
|
|
+// Navigate via UNO — used only for start/end and reading position
|
|
|
+function pyUnoNav(direction: "start" | "end"): Promise<string> {
|
|
|
const scripts: Record<string, string> = {
|
|
|
- next: `
|
|
|
-import sys, time
|
|
|
-try:
|
|
|
- import uno
|
|
|
- localCtx = uno.getComponentContext()
|
|
|
- resolver = localCtx.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localCtx)
|
|
|
- ctx = resolver.resolve("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")
|
|
|
- smgr = ctx.ServiceManager
|
|
|
- desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
|
|
|
- comp = desktop.getCurrentComponent()
|
|
|
- controller = comp.getCurrentController()
|
|
|
- draw = comp.DrawPages
|
|
|
- print(f"[nav] next, controller: {type(controller).__name__}", file=sys.stderr)
|
|
|
- if hasattr(controller, 'gotoNextSlide'):
|
|
|
- controller.gotoNextSlide()
|
|
|
- else:
|
|
|
- page = controller.getCurrentPage()
|
|
|
- idx = 0
|
|
|
- for i in range(draw.Count):
|
|
|
- if draw.getByIndex(i) == page:
|
|
|
- idx = i
|
|
|
- break
|
|
|
- next_idx = min(idx + 1, draw.Count - 1)
|
|
|
- print(f"[nav] setCurrentPage {idx} -> {next_idx}", file=sys.stderr)
|
|
|
- controller.setCurrentPage(draw.getByIndex(next_idx))
|
|
|
- time.sleep(0.3)
|
|
|
- ${readPos}
|
|
|
-except Exception as e:
|
|
|
- import traceback; traceback.print_exc(file=sys.stderr)
|
|
|
- print("0/0")
|
|
|
-`,
|
|
|
- prev: `
|
|
|
-import sys, time
|
|
|
-try:
|
|
|
- import uno
|
|
|
- localCtx = uno.getComponentContext()
|
|
|
- resolver = localCtx.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localCtx)
|
|
|
- ctx = resolver.resolve("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")
|
|
|
- smgr = ctx.ServiceManager
|
|
|
- desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
|
|
|
- comp = desktop.getCurrentComponent()
|
|
|
- controller = comp.getCurrentController()
|
|
|
- draw = comp.DrawPages
|
|
|
- print(f"[nav] prev, controller: {type(controller).__name__}", file=sys.stderr)
|
|
|
- if hasattr(controller, 'gotoPreviousSlide'):
|
|
|
- controller.gotoPreviousSlide()
|
|
|
- else:
|
|
|
- page = controller.getCurrentPage()
|
|
|
- idx = 0
|
|
|
- for i in range(draw.Count):
|
|
|
- if draw.getByIndex(i) == page:
|
|
|
- idx = i
|
|
|
- break
|
|
|
- prev_idx = max(idx - 1, 0)
|
|
|
- print(f"[nav] setCurrentPage {idx} -> {prev_idx}", file=sys.stderr)
|
|
|
- controller.setCurrentPage(draw.getByIndex(prev_idx))
|
|
|
- time.sleep(0.3)
|
|
|
- ${readPos}
|
|
|
-except Exception as e:
|
|
|
- import traceback; traceback.print_exc(file=sys.stderr)
|
|
|
- print("0/0")
|
|
|
-`,
|
|
|
start: `
|
|
|
import sys, time
|
|
|
try:
|
|
|
@@ -241,29 +181,21 @@ try:
|
|
|
desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
|
|
|
comp = desktop.getCurrentComponent()
|
|
|
pres = comp.Presentation
|
|
|
- print(f"[nav] starting presentation", file=sys.stderr)
|
|
|
+ print("[nav] starting presentation", file=sys.stderr)
|
|
|
pres.start()
|
|
|
time.sleep(1.5)
|
|
|
controller = comp.getCurrentController()
|
|
|
draw = comp.DrawPages
|
|
|
total = draw.Count
|
|
|
- current = 0
|
|
|
+ current = 1
|
|
|
print(f"[nav] controller after start: {type(controller).__name__}", file=sys.stderr)
|
|
|
if hasattr(controller, 'getCurrentSlideIndex'):
|
|
|
current = controller.getCurrentSlideIndex() + 1
|
|
|
- else:
|
|
|
- try:
|
|
|
- page = controller.getCurrentPage()
|
|
|
- for i in range(draw.Count):
|
|
|
- if draw.getByIndex(i) == page:
|
|
|
- current = i + 1
|
|
|
- break
|
|
|
- except:
|
|
|
- current = 1
|
|
|
print(f"[nav] start -> {current}/{total}", file=sys.stderr)
|
|
|
print(f"{current}/{total}")
|
|
|
except Exception as e:
|
|
|
- import traceback; traceback.print_exc(file=sys.stderr)
|
|
|
+ import traceback
|
|
|
+ traceback.print_exc(file=sys.stderr)
|
|
|
print("0/0")
|
|
|
`,
|
|
|
end: `
|
|
|
@@ -287,27 +219,27 @@ try:
|
|
|
print(f"[nav] end -> 0/{total}", file=sys.stderr)
|
|
|
print(f"0/{total}")
|
|
|
except Exception as e:
|
|
|
- import traceback; traceback.print_exc(file=sys.stderr)
|
|
|
+ import traceback
|
|
|
+ traceback.print_exc(file=sys.stderr)
|
|
|
print("0/0")
|
|
|
`,
|
|
|
};
|
|
|
-
|
|
|
return pyUno(scripts[direction]);
|
|
|
}
|
|
|
|
|
|
const LinuxDriver: Driver = {
|
|
|
async next() {
|
|
|
- console.log("[linux] next: navigating via UNO");
|
|
|
- const raw = await pyUnoNav("next");
|
|
|
- console.log(`[linux] next raw: "${raw}"`);
|
|
|
- return parseSlide(raw);
|
|
|
+ console.log("[linux] next: sending Right key via xdotool");
|
|
|
+ await xdotoolNav("Right");
|
|
|
+ await Bun.sleep(300);
|
|
|
+ return LinuxDriver.status();
|
|
|
},
|
|
|
|
|
|
async prev() {
|
|
|
- console.log("[linux] prev: navigating via UNO");
|
|
|
- const raw = await pyUnoNav("prev");
|
|
|
- console.log(`[linux] prev raw: "${raw}"`);
|
|
|
- return parseSlide(raw);
|
|
|
+ console.log("[linux] prev: sending Left key via xdotool");
|
|
|
+ await xdotoolNav("Left");
|
|
|
+ await Bun.sleep(300);
|
|
|
+ return LinuxDriver.status();
|
|
|
},
|
|
|
|
|
|
async start() {
|