diff --git a/randopix.nimble b/randopix.nimble index 7767e98..68e1f3d 100644 --- a/randopix.nimble +++ b/randopix.nimble @@ -6,16 +6,13 @@ author = "luxick" description = "Play an image slide show from different sources" license = "GPL-2.0" srcDir = "src" -bin = @["randopix"] +bin = @["randopix", "pixctrl"] # Dependencies requires "nim >= 1.0.0", "gintro >= 0.5.5", "argparse >=0.10.1" task debug, "Compile debug version": - exec "nim c -d:debug --debugger:native --out:bin/randopix src/randopix.nim" - -task r, "Compile and run": - exec "nim c -r --out:bin/randopix src/randopix.nim" + exec "nim c -d:debug --debugger:native src/randopix.nim" task release, "Compile release version": - exec fmt"nim c -d:release --out:bin/{version}/randopix src/randopix.nim" + exec fmt"nim c -d:release --out:randopix-{version} src/randopix.nim" diff --git a/src/commands.nim b/src/commands.nim new file mode 100644 index 0000000..ee9c0ee --- /dev/null +++ b/src/commands.nim @@ -0,0 +1,15 @@ + +const + defaultPort* = 5555 ## Default port at which the control server will run + +type + Command* {.pure.} = enum + Close = "close" ## Closes the control server and exists the applicaiton + Refresh = "refresh" ## Force refresh of the image now + + CommandMessage* = object + command*: Command ## Command that the application should execute + parameter*: string ## Optional parameter for the command + +proc newCommand*(c: Command, p: string = ""): CommandMessage = + CommandMessage(command: c, parameter: p) \ No newline at end of file diff --git a/src/pixctrl.nim b/src/pixctrl.nim new file mode 100644 index 0000000..bccf068 --- /dev/null +++ b/src/pixctrl.nim @@ -0,0 +1,13 @@ +import net +import argparse +import commands + +var p = newParser("pixctrl"): + help("Control utilitiy for randopix") + option("-s", "--server", help="Host running the randopix server", default="127.0.0.1") + option("-p", "--port", help="Port to connect to the randopix server", default = $defaultPort) + +var socket = newSocket() +socket.connect("127.0.0.1", Port(defaultPort)) +socket.send("Hello, Sockets!\r\L") +socket.close() \ No newline at end of file diff --git a/src/randopix.nim b/src/randopix.nim index 45b6d3a..8a7088d 100644 --- a/src/randopix.nim +++ b/src/randopix.nim @@ -1,8 +1,10 @@ import os, options, strformat -import gintro/[gtk, glib, gobject, gio, gdkpixbuf] +import gintro/[glib, gobject, gdkpixbuf] import gintro/gdk except Window +import gintro/gtk except newSocket, Socket +import gintro/gio except Socket import argparse except run -import providers +import providers, server, commands const css = slurp("app.css") @@ -21,6 +23,8 @@ var window: ApplicationWindow imageWidget: Image label: Label + # Server vor recieving commands from external tools + serverWorker: system.Thread[void] proc enumToStrings(en: typedesc): seq[string] = for x in en: @@ -59,7 +63,7 @@ proc newArgs(): Option[Args] = fullscreen: not opts.windowed, verbose: opts.verbose, timeout: timeout)) - except UsageError: + except: echo getCurrentExceptionMsg() echo p.help @@ -106,12 +110,27 @@ proc forceUpdate(action: SimpleAction; parameter: Variant;) = proc timedUpdate(image: Widget): bool = let ok = updateImage(); - if ok: - return true; - else: + if not ok: label.notify "Error while refreshing image, retrying..." - discard timeoutAdd(uint32(args.timeout), timedUpdate, imageWidget) - return false + discard timeoutAdd(uint32(args.timeout), timedUpdate, imageWidget) + return false + +proc checkServerChannel(parameter: string): bool = + ## Check the channel from the control server for incomming commands + let tried = chan.tryRecv() + + if tried.dataAvailable: + let msg: CommandMessage = tried.msg + echo "Main app got message: ", msg.command + + case msg.command + of Command.Refresh: + discard updateImage() + else: + echo "Command ignored", msg.command + + result = false + discard idleAdd(checkServerChannel, parameter) proc toggleFullscreen(action: SimpleAction; parameter: Variant; window: ApplicationWindow) = ## Fullscreen toggle event @@ -121,12 +140,21 @@ proc toggleFullscreen(action: SimpleAction; parameter: Variant; window: Applicat window.fullscreen args.fullscreen = not args.fullscreen +proc cleanUp(w: ApplicationWindow, app: Application) = + ## Stop the control server and exit the GTK application + echo "Stopping control server..." + closeServer() + serverWorker.joinThread() + chan.close() + echo "Server stopped." + app.quit() + proc quit(action: SimpleAction; parameter: Variant; app: Application) = ## Application quit event - app.quit() + cleanUp(window, app) proc connectSignals(app: Application) = - ## Connect th GTK signals to the procs + ## Connect the GTK signals to the procs let fullscreenAction = newSimpleAction("fullscreen") discard fullscreenAction.connect("activate", toggleFullscreen, window) app.setAccelsForAction("win.fullscreen", "F") @@ -142,6 +170,8 @@ proc connectSignals(app: Application) = app.setAccelsForAction("win.update", "U") window.actionMap.addAction(updateImageAction) + window.connect("destroy", cleanUp, app) + proc appActivate(app: Application) = # Parse arguments from the command line let parsed = newArgs() @@ -152,7 +182,7 @@ proc appActivate(app: Application) = window = newApplicationWindow(app) window.title = "randopix" - window.setKeepAbove(true) + window.setKeepAbove(false) window.setDefaultSize(800, 600) # Custom styling for e.g. the background color CSS data is in the "style.nim" module @@ -177,8 +207,15 @@ proc appActivate(app: Application) = app.connectSignals window.showAll - discard updateImage() - discard timeoutAdd(uint32(args.timeout), timedUpdate, imageWidget) + discard timeoutAdd(500, timedUpdate, imageWidget) + + ## open communication channel from the control server + chan.open() + + ## Start the server for handling incoming commands + createThread(serverWorker, runServer) + var tag = "" + discard idleAdd(checkServerChannel, tag) when isMainModule: let app = newApplication("org.luxick.randopix") diff --git a/src/server.nim b/src/server.nim new file mode 100644 index 0000000..3d37d8c --- /dev/null +++ b/src/server.nim @@ -0,0 +1,40 @@ +import net, json, marshal +import commands + +var + chan*: Channel[CommandMessage] + +proc closeServer*() = + ## Sends a "Close" command to the server + var socket = newSocket() + socket.connect("127.0.0.1", Port(defaultPort)) + let c = newCommand(Command.Close) + socket.send($(%c)) + socket.close() + +proc runServer*() = + var socket = newSocket() + socket.bindAddr(Port(defaultPort)) + socket.listen() + echo "Control server is listening" + + while true: + # Process client requests + var client = newSocket() + socket.accept(client) + echo("Incomming client") + try: + var line = client.recvLine() + let msg = to[CommandMessage](line) + case msg.command + of Command.Close: + echo "Server recieved termination command. Exiting." + break + else: + # Pass command from client to main applicaiton + chan.send(msg) + + except OSError: + echo "Server error: ", getCurrentExceptionMsg() + except: + echo "Invalid command from client" \ No newline at end of file diff --git a/src/server.nim.cfg b/src/server.nim.cfg new file mode 100644 index 0000000..6c1ded9 --- /dev/null +++ b/src/server.nim.cfg @@ -0,0 +1 @@ +threads:on \ No newline at end of file