diff --git a/imagemagick-clock/README.md b/imagemagick-clock/README.md new file mode 100644 index 0000000..cf9c91e --- /dev/null +++ b/imagemagick-clock/README.md @@ -0,0 +1,53 @@ +# ImageMagick Clock + +ImageMagick Clock draws a custom clock using ImageMagick (or any other CLI image drawing tool). + +## Plugin + +| Field | Value | +| --- | --- | +| ID | `jinliu/imagemagick-clock` | +| Entries | Bar widget: `clock` | + +## Requirements + +* `magick` from `ImageMagick`. +* `mv` from `coreutils`. +* (Optional) [7-Segment font](https://www.dafont.com/seven-segment.font) for a classic digital clock look. + +## Usage + +Add `ImageMagick Clock` from the Add-widget picker. The widget displays a +custom clock image generated by ImageMagick (or any other CLI image drawing tool). + +The default command line is: + +```bash +magick -size 270x100 canvas:black -transparent black -pointsize 128 -fill '#FF2400' -draw 'text 0,100 \"%H\"' -fill white -pointsize 84 -draw 'text 120,100 \":%M\"' -fill '#FF2400' -stroke '#FF2400' -strokewidth 5 -draw 'line 225,0 225,100' -strokewidth 0 -stroke white -fill white -pointsize 32 -draw 'text 235, 25 \"%m\"' -draw 'text 235, 60 \"%d\"' -draw 'text 235, 95 \"%a\"' '%output_file' +``` + +You can customize the command line in the widget's configuration to change how the clock looks. The "%output_file" placeholder will be replaced with the path to the file where the clock image should be saved. Other placeholders like "%H", "%M", "%a" will be replaced with the hour, minute, and day of the week, etc. See `man strftime` for the complete list of placeholders. + +You might want to install the `7-Segment` font to get a classic digital clock look. Add `-font '7-Segment'` to the command line after `magick` to use it. The command line would then look like this: + +```bash +magick -font '7-Segment' -size 270x100 canvas:black -transparent black -pointsize 128 -fill '#FF2400' -draw 'text 0,100 \"%H\"' -fill white -pointsize 84 -draw 'text 120,100 \":%M\"' -fill '#FF2400' -stroke '#FF2400' -strokewidth 5 -draw 'line 225,0 225,100' -strokewidth 0 -stroke white -fill white -pointsize 32 -draw 'text 235, 25 \"%m\"' -draw 'text 235, 60 \"%d\"' -draw 'text 235, 95 \"%a\"' '%output_file' +``` + +If your locale uses characters that are not supported by the `7-Segment` font (e.g., Chinese), you might want to set a different font before the last `-draw` command. For example: + +```bash +magick -font '7-Segment' -size 270x100 canvas:black -transparent black -pointsize 128 -fill '#FF2400' -draw 'text 0,100 \"%H\"' -fill white -pointsize 84 -draw 'text 120,100 \":%M\"' -fill '#FF2400' -stroke '#FF2400' -strokewidth 5 -draw 'line 225,0 225,100' -strokewidth 0 -stroke white -fill white -pointsize 32 -draw 'text 235, 25 \"%m\"' -draw 'text 235, 60 \"%d\"' -stroke none -font Noto-Sans-CJK-SC-Bold -draw 'text 235, 95 \"%a\"' '%output_file' +``` + +(Note: You can list all available fonts on your system by running `magick -list font`.) + +You can use any command-line drawing tool, not just ImageMagick, as long as it generates an image file at the specified output path. The plugin will call the command at the beginning of every minute to generate a new clock image for the next minute. Therefore, your command can take up to a full minute to generate one image, though I wouldn't recommend something that heavy. + +## Settings + +| Setting | Type | Default | Description | +| --- | --- | --- | --- | +| `command_line` | `string` | (too long, omitted) | The command line to draw the clock | +| `image_width` | `int` | 54 | The width of the clock image | +| `image_height` | `int` | 20 | The height of the clock image | diff --git a/imagemagick-clock/plugin.toml b/imagemagick-clock/plugin.toml new file mode 100644 index 0000000..b1fbea2 --- /dev/null +++ b/imagemagick-clock/plugin.toml @@ -0,0 +1,39 @@ +id = "jinliu/imagemagick-clock" +name = "ImageMagick Clock" +version = "1.0.0" +plugin_api = 3 +author = "Jin Liu" +tags = ["clock"] +icon = "clock" +description = "A custom clock drawn with ImageMagick." +dependencies = ["magick", "mv"] +license = "MIT" + +[[widget]] +id = "clock" +entry = "widget.luau" + + [[widget.setting]] + key = "command_line" + type = "string" + label_key = "settings.cmdline.label" + description_key = "settings.cmdline.description" + default = "magick -size 270x100 canvas:black -transparent black -pointsize 128 -fill '#FF2400' -draw 'text 0,100 \"%H\"' -fill white -pointsize 84 -draw 'text 120,100 \":%M\"' -fill '#FF2400' -stroke '#FF2400' -strokewidth 5 -draw 'line 225,0 225,100' -strokewidth 0 -stroke white -fill white -pointsize 32 -draw 'text 235, 25 \"%m\"' -draw 'text 235, 60 \"%d\"' -draw 'text 235, 95 \"%a\"' '%output_file'" + + [[widget.setting]] + key = "image_width" + type = "int" + label_key = "settings.image_width.label" + description_key = "settings.image_width.description" + default = 54 + min = 1 + max = 1000 + + [[widget.setting]] + key = "image_height" + type = "int" + label_key = "settings.image_height.label" + description_key = "settings.image_height.description" + default = 20 + min = 1 + max = 1000 diff --git a/imagemagick-clock/thumbnail.webp b/imagemagick-clock/thumbnail.webp new file mode 100644 index 0000000..b12126b Binary files /dev/null and b/imagemagick-clock/thumbnail.webp differ diff --git a/imagemagick-clock/translations/en.json b/imagemagick-clock/translations/en.json new file mode 100644 index 0000000..6c8260f --- /dev/null +++ b/imagemagick-clock/translations/en.json @@ -0,0 +1,16 @@ +{ + "settings": { + "cmdline": { + "label": "Command line", + "description": "The command should generate an image file to be displayed as the clock. `%output_file` will be replaced with the path to the output file. Then `%H`, `%M`, etc. will be replaced with the current time values. See `man strftime` for more formatting options." + }, + "image_width": { + "label": "Image width", + "description": "The display width of the clock image in logical pixels. (Not the width of the generated image file.)" + }, + "image_height": { + "label": "Image height", + "description": "The display height of the clock image in logical pixels. (Not the height of the generated image file.)" + } + } +} diff --git a/imagemagick-clock/widget.luau b/imagemagick-clock/widget.luau new file mode 100644 index 0000000..e592295 --- /dev/null +++ b/imagemagick-clock/widget.luau @@ -0,0 +1,91 @@ +local CMDLINE_TEMPLATE = noctalia.getConfig("command_line") +local IMAGE_WIDTH = noctalia.getConfig("image_width") +local IMAGE_HEIGHT = noctalia.getConfig("image_height") + +local OUTPUT_DIR = noctalia.pluginDataDir() +local CURRENT_CLOCK_FACE_PATH = OUTPUT_DIR .. "/current.png" +local NEXT_CLOCK_FACE_PATH = OUTPUT_DIR .. "/next.png" + +local generatingClockFace = false +local movingClockFace = false +local nextClockFaceReady = false +local nextClockFaceTimestamp = nil + +local function drawClockFace(timestamp) + if generatingClockFace then + return + end + + generatingClockFace = true + + local cmdline = string.gsub(CMDLINE_TEMPLATE, "%%output_file", NEXT_CLOCK_FACE_PATH) + cmdline = os.date(cmdline, timestamp) + -- print("Running command: " .. cmdline) + + noctalia.runAsync(cmdline, function(result) + generatingClockFace = false + nextClockFaceTimestamp = timestamp + if result.exitCode == 0 then + nextClockFaceReady = true + else + print("Error generating clock face: " .. result.stderr .. " (command: " .. cmdline .. ") " .. "output: " .. result.stdout) + nextClockFaceReady = false + end + end) +end + +local function drawCurrentClockFace() + local timestamp = os.time() + local time = os.date("*t", timestamp) + local currentTimestamp = timestamp - time.sec + -- print("Drawing current clock face at " .. os.date("%H:%M:%S", nextTimestamp)) + drawClockFace(currentTimestamp) +end + +local function drawNextClockFace() + local timestamp = os.time() + local time = os.date("*t", timestamp) + local nextTimestamp = timestamp - time.sec + 60 + -- print("Drawing next clock face at " .. os.date("%H:%M:%S", nextTimestamp)) + drawClockFace(nextTimestamp) +end + +function update() + local timestamp = os.time() + if generatingClockFace or movingClockFace or (nextClockFaceTimestamp and timestamp < nextClockFaceTimestamp and timestamp >= nextClockFaceTimestamp - 60) then + return + end + + if not nextClockFaceTimestamp or timestamp >= nextClockFaceTimestamp + 60 or timestamp < nextClockFaceTimestamp - 60 then + -- Next clock face unavailable (startup), or doesn't match current time (resume from sleep, or system clock changed). + -- Either case, draw the current clock face immediately. + drawCurrentClockFace() + return + end + + if nextClockFaceReady then + nextClockFaceReady = false + movingClockFace = true + noctalia.runAsync("mv -f '" .. NEXT_CLOCK_FACE_PATH .. "' '" .. CURRENT_CLOCK_FACE_PATH .. "'", function(result) + movingClockFace = false + if result.exitCode == 0 then + barWidget.setImage(CURRENT_CLOCK_FACE_PATH, true, IMAGE_WIDTH, IMAGE_HEIGHT) + else + print("Error moving new clock face: " .. result.stderr .. " (command: mv -f " .. NEXT_CLOCK_FACE_PATH .. " " .. CURRENT_CLOCK_FACE_PATH .. ") " .. "output: " .. result.stdout) + end + drawNextClockFace() + end) + else + drawNextClockFace() + end +end + +function onClick() + noctalia.runAsync("noctalia msg panel-toggle control-center calendar", function(result) + if result.exitCode ~= 0 then + print("Error toggling calendar panel: " .. result.stderr .. " (command: noctalia msg panel-toggle control-center calendar) " .. "output: " .. result.stdout) + end + end) +end + +noctalia.setUpdateInterval(1000)