Merge remote-tracking branch 'ni/master'

This commit is contained in:
lassulus 2023-02-09 09:39:02 +01:00
commit 4dc1605366
27 changed files with 1081 additions and 452 deletions

View File

@ -7,13 +7,13 @@
, servant-server, split, terminal-size, text, time, transformers , servant-server, split, terminal-size, text, time, transformers
, transformers-compat, unix, vector, wai, warp , transformers-compat, unix, vector, wai, warp
}: }:
mkDerivation { mkDerivation rec {
pname = "much"; pname = "much";
version = "1.3.1"; version = "1.3.2";
src = fetchgit { src = fetchgit {
url = "https://cgit.krebsco.de/much"; url = "https://cgit.krebsco.de/much";
sha256 = "0gwyhqcvg9ywna8fhb9hnx97qh5inglj3l0pcwkgwcvm27mfpcqa"; hash = "sha256-q65EYO1d3NYVv2NECkGWPb1TyHGdARNi/GX4pgQmljc=";
rev = "77357335a3a88a4b93f91a46ab939a1a9b192977"; rev = "refs/tags/${version}";
fetchSubmodules = true; fetchSubmodules = true;
}; };
isLibrary = true; isLibrary = true;

View File

@ -1,21 +1,22 @@
{ mkDerivation, base, blessings, bytestring, containers { mkDerivation, aeson, base, blessings, bytestring, containers
, data-default, hack, lib, optparse-applicative, probability , data-default, extra, fetchgit, hack, lib, optparse-applicative
, scanner, speculate, split, terminal-size, text, unix, X11 , probability, scanner, speculate, split, terminal-size, text, unix
, fetchgit , utf8-string, X11
}: }:
mkDerivation { mkDerivation {
pname = "pager"; pname = "pager";
version = "1.0.0"; version = "1.0.0";
src = fetchgit { src = fetchgit {
url = "https://cgit.krebsco.de/pager"; url = "https://cgit.krebsco.de/pager";
sha256 = "1qlkhqidaa6w02ix9ambfdsm7lfyx30ap481b9ic1ppyfkhqzfp6"; sha256 = "07wjlhnb27vfhkqq5vhi768mlrcpwl4b2yfk04v3lw047q6pmby0";
rev = "fc6105a5e7d1e3a07bf07ea85e7902dd8e9fc849"; rev = "dfa3ff346d22d332ffbadd46963f1cc5cb2a4939";
fetchSubmodules = true; fetchSubmodules = true;
}; };
isLibrary = false; isLibrary = true;
isExecutable = true; isExecutable = true;
libraryHaskellDepends = [ base extra utf8-string X11 ];
executableHaskellDepends = [ executableHaskellDepends = [
base blessings bytestring containers data-default hack aeson base blessings bytestring containers data-default hack
optparse-applicative probability scanner speculate split optparse-applicative probability scanner speculate split
terminal-size text unix X11 terminal-size text unix X11
]; ];

View File

@ -0,0 +1,416 @@
{ config, pkgs, ... }:
with pkgs.stockholm.lib;
let
# Encode integer to C-escaped string of bytes, little endian / LSB 0
le = rec {
x1 = i: let
i0 = mod i 16;
i1 = i / 16;
in
if i == 0 then
"\\0"
else if i < 16 then
"\\x${elemAt hexchars i0}"
else
"\\x${elemAt hexchars i1}${elemAt hexchars i0}";
x2 = i: let
i0 = mod i 256;
i1 = i / 256;
in
"${x1 i1}${x1 i0}";
x4 = i: let
i0 = mod i 65536;
i1 = i / 65536;
in
"${x2 i1}${x2 i0}";
};
toQList = t: xs:
assert t == "int";
"QList<${t}>${le.x4 0}${le.x1 (length xs)}${concatMapStrings le.x4 xs}";
in
{
options = {
imgur = mkOption {
default = {};
type = types.submodule {
options = {
enable = mkEnableOption "imgur";
createUrl = mkOption {
example = "http://p.r/image";
type = types.str;
};
deleteUrl = mkOption {
example = "http://p.r/image/delete/%1";
type = types.str;
};
xdg-open = mkOption {
default = {};
type = types.submodule {
options = {
enable = mkEnableOption "imgur.xdg-open" // {
default = true;
};
browser = mkOption {
default = "${pkgs.coreutils}/bin/false";
type = types.str;
};
createPrefix = mkOption {
default = config.imgur.createUrl;
type = types.str;
};
deletePrefix = mkOption {
default = removeSuffix "/%1" config.imgur.deleteUrl;
type = types.str;
};
};
};
};
};
};
};
package = mkOption {
type = types.package;
default = import ./flameshot { inherit pkgs; };
};
settings = {
# Options without a description are not documented in flameshot's README.
# Compare with:
# nix-shell -p flameshot-once.dev --run get-recognizedGeneralOptions
General = mapAttrs (_: recursiveUpdate { default = null; }) {
allowMultipleGuiInstances = mkOption {
description = ''
Allow multiple instances of `flameshot gui` to run at the same time
'';
type = with types; nullOr bool;
};
antialiasingPinZoom = mkOption {
description = ''
Anti-aliasing image when zoom the pinned image
'';
type = with types; nullOr bool;
};
autoCloseIdleDaemon = mkOption {
description = ''
Automatically close daemon when it's not needed
'';
type = with types; nullOr bool;
};
buttons = let
buttonTypes = {
# Generated with:
# nix-shell -p flameshot-once.dev --run get-buttonTypes
TYPE_PENCIL = 0;
TYPE_DRAWER = 1;
TYPE_ARROW = 2;
TYPE_SELECTION = 3;
TYPE_RECTANGLE = 4;
TYPE_CIRCLE = 5;
TYPE_MARKER = 6;
TYPE_SELECTIONINDICATOR = 7;
TYPE_MOVESELECTION = 8;
TYPE_UNDO = 9;
TYPE_COPY = 10;
TYPE_SAVE = 11;
TYPE_EXIT = 12;
TYPE_IMAGEUPLOADER = 13;
TYPE_OPEN_APP = 14;
TYPE_PIXELATE = 15;
TYPE_REDO = 16;
TYPE_PIN = 17;
TYPE_TEXT = 18;
TYPE_CIRCLECOUNT = 19;
TYPE_SIZEINCREASE = 20;
TYPE_SIZEDECREASE = 21;
TYPE_INVERT = 22;
TYPE_ACCEPT = 23;
};
iterableButtonTypes = [
# Generated with:
# nix-shell -p flameshot-once.dev --run get-iterableButtonTypes
"TYPE_ACCEPT"
"TYPE_ARROW"
"TYPE_CIRCLE"
"TYPE_CIRCLECOUNT"
"TYPE_COPY"
"TYPE_DRAWER"
"TYPE_EXIT"
"TYPE_IMAGEUPLOADER"
"TYPE_MARKER"
"TYPE_MOVESELECTION"
"TYPE_OPEN_APP"
"TYPE_PENCIL"
"TYPE_PIN"
"TYPE_PIXELATE"
"TYPE_RECTANGLE"
"TYPE_REDO"
"TYPE_SAVE"
"TYPE_SELECTION"
"TYPE_SIZEDECREASE"
"TYPE_SIZEINCREASE"
"TYPE_TEXT"
"TYPE_UNDO"
];
in mkOption {
apply = names:
if names != null then let
values = map (name: buttonTypes.${name}) names;
in
''@Variant(\0\0\0\x7f\0\0\0\v${toQList "int" values})''
else
null;
description = ''
Configure which buttons to show after drawing a selection
'';
type = with types; nullOr (listOf (enum iterableButtonTypes));
};
checkForUpdates = mkOption {
type = with types; nullOr bool;
};
contrastOpacity = mkOption {
description = ''
Opacity of area outside selection
'';
type = with types; nullOr (boundedInt 0 255);
};
contrastUiColor = mkOption {
description = ''
Contrast UI color
'';
type = with types; nullOr flameshot.color;
};
copyAndCloseAfterUpload = mkOption {
type = with types; nullOr bool;
};
copyOnDoubleClick = mkOption {
type = with types; nullOr bool;
};
copyPathAfterSave = mkOption {
description = ''
Copy path to image after save
'';
type = with types; nullOr bool;
};
copyURLAfterUpload = mkOption {
description = ''
On successful upload, close the dialog and copy URL to clipboard
'';
type = with types; nullOr bool;
};
disabledTrayIcon = mkOption {
description = ''
Whether the tray icon is disabled
'';
type = with types; nullOr bool;
};
drawColor = mkOption {
description = ''
Last used color
'';
type = with types; nullOr flameshot.color;
};
drawFontSize = mkOption {
type = with types; nullOr positive;
};
drawThickness = mkOption {
description = ''
Last used tool thickness
'';
type = with types; nullOr positive;
};
filenamePattern = mkOption {
description = ''
Filename pattern using C++ strftime formatting
'';
type =
# This is types.filename extended by [%:][%:+]*
with types;
nullOr (addCheck str (test "[%:0-9A-Za-z._][%:+0-9A-Za-z._-]*"));
};
fontFamily = mkOption {
type = with types; nullOr str;
};
historyConfirmationToDelete = mkOption {
type = with types; nullOr bool;
};
ignoreUpdateToVersion = mkOption {
description = ''
Ignore updates to versions less than this value
'';
type = with types; nullOr str;
};
keepOpenAppLauncher = mkOption {
description = ''
Keep the App Launcher open after selecting an app
'';
type = with types; nullOr bool;
};
predefinedColorPaletteLarge = mkOption {
description = ''
Use larger color palette as the default one
'';
type = with types; nullOr bool;
};
saveAfterCopy = mkOption {
description = ''
Save image after copy
'';
type = with types; nullOr bool;
};
saveAsFileExtension = mkOption {
description = ''
Default file extension for screenshots
'';
type = with types; nullOr (addCheck filename (hasPrefix "."));
};
safeLastRegion = mkOption {
type = with types; nullOr bool;
};
savePath = mkOption {
description = ''
Image Save Path
'';
type = with types; nullOr absolute-pathname;
};
savePathFixed = mkOption {
description = ''
Whether the savePath is a fixed path
'';
type = with types; nullOr bool;
};
showDesktopNotification = mkOption {
description = ''
Show desktop notifications
'';
type = with types; nullOr bool;
};
showHelp = mkOption {
description = ''
Show the help screen on startup
'';
type = with types; nullOr bool;
};
showMagnifier = mkOption {
type = with types; nullOr bool;
};
showSelectionGeometry = mkOption {
type = with types; nullOr (boundedInt 0 5);
};
showSelectionGeometryHideTime = mkOption {
type = with types; nullOr uint;
};
showSidePanelButton = mkOption {
description = ''
Show the side panel button
'';
type = with types; nullOr bool;
};
showStartupLaunchMessage = mkOption {
type = with types; nullOr bool;
};
squareMagnifier = mkOption {
type = with types; nullOr bool;
};
startupLaunch = mkOption {
description = ''
Launch at startup
'';
type = with types; nullOr bool;
};
uiColor = mkOption {
description = ''
Main UI color
'';
type = with types; nullOr flameshot.color;
};
undoLimit = mkOption {
type = with types; nullOr (boundedInt 0 999);
};
uploadClientSecret = mkOption {
type = with types; nullOr str;
};
uploadHistoryMax = mkOption {
type = with types; nullOr uint;
};
uploadWithoutConfirmation = mkOption {
description = ''
Upload to imgur without confirmation
'';
type = with types; nullOr bool;
};
useJpgForClipboard = mkOption {
description = ''
Use JPG format instead of PNG
'';
type = with types; nullOr bool;
};
userColors = mkOption {
apply = value:
if value != null then
concatStringsSep ", " value
else
null;
description = ''
List of colors for color picker
The colors are arranged counter-clockwise with the first being set
to the right of the cursor. "picker" adds a custom color picker.
'';
type =
with types;
nullOr (listOf (either flameshot.color (enum ["picker"])));
};
};
Shortcuts = genAttrs [
# Generated with:
# nix-shell -p flameshot-once.dev --run get-Shortcuts
"TYPE_ACCEPT"
"TYPE_ARROW"
"TYPE_CIRCLE"
"TYPE_CIRCLECOUNT"
"TYPE_COMMIT_CURRENT_TOOL"
"TYPE_COPY"
"TYPE_DELETE_CURRENT_TOOL"
"TYPE_DRAWER"
"TYPE_EXIT"
"TYPE_IMAGEUPLOADER"
"TYPE_INVERT"
"TYPE_MARKER"
"TYPE_MOVESELECTION"
"TYPE_MOVE_DOWN"
"TYPE_MOVE_LEFT"
"TYPE_MOVE_RIGHT"
"TYPE_MOVE_UP"
"TYPE_OPEN_APP"
"TYPE_PENCIL"
"TYPE_PIN"
"TYPE_PIXELATE"
"TYPE_RECTANGLE"
"TYPE_REDO"
"TYPE_RESIZE_DOWN"
"TYPE_RESIZE_LEFT"
"TYPE_RESIZE_RIGHT"
"TYPE_RESIZE_UP"
"TYPE_SAVE"
"TYPE_SELECTION"
"TYPE_SELECTIONINDICATOR"
"TYPE_SELECT_ALL"
"TYPE_SIZEDECREASE"
"TYPE_SIZEINCREASE"
"TYPE_SYM_RESIZE_DOWN"
"TYPE_SYM_RESIZE_LEFT"
"TYPE_SYM_RESIZE_RIGHT"
"TYPE_SYM_RESIZE_UP"
"TYPE_TEXT"
"TYPE_TOGGLE_PANEL"
"TYPE_UNDO"
] (name: mkOption {
default = null;
type = with types; nullOr str;
});
};
};
}

View File

@ -0,0 +1,149 @@
{ name ? "flameshot-once", pkgs, ... }@args:
with pkgs.stockholm.lib;
let
# config cannot be declared in the input attribute set because that would
# cause callPackage to inject the wrong config. Instead, get it from ...
# via args.
config = args.config or {};
cfg = evalModulesConfig (singleton {
_file = toString ./default.nix;
_module.args.pkgs = pkgs;
imports = [
config
./config.nix
];
});
in
pkgs.symlinkJoin {
inherit name;
paths = [
(pkgs.write "flameshot-once" {
"/bin/flameshot-once" = {
executable = true;
text = /* sh */ ''
#! ${pkgs.dash}/bin/dash
export PATH=${makeBinPath [
pkgs.qt5.qtbase
]}:''${PATH+:$PATH}
${optionalString (config != null) /* sh */ ''
export XDG_CONFIG_HOME=${placeholder "out"}/etc
${optionalString cfg.imgur.enable /* sh */ ''
export IMGUR_CREATE_URL=${shell.escape cfg.imgur.createUrl}
export IMGUR_DELETE_URL=${shell.escape cfg.imgur.deleteUrl}
${optionalString cfg.imgur.xdg-open.enable /* sh */ ''
export PATH=${placeholder "out"}/lib/imgur/bin''${PATH+:$PATH}
''}
''}
''}
${cfg.package}/bin/flameshot &
exec ${cfg.package}/bin/flameshot gui
'';
};
"/etc/flameshot/flameshot.ini".text =
lib.generators.toINI {} (stripAttr cfg.settings);
${if cfg.imgur.enable then "/lib/imgur/bin/xdg-open" else null} = {
executable = true;
text = /* sh */ ''
#! ${pkgs.dash}/bin/dash
set -efu
uri=$1
prefix=$(${pkgs.coreutils}/bin/dirname "$uri")
case $prefix in
(${shell.escape cfg.imgur.xdg-open.createPrefix})
echo "opening image in browser: $uri" >&2
exec ${config.imgur.xdg-open.browser} "$uri"
;;
(${shell.escape cfg.imgur.xdg-open.deletePrefix})
echo "deleting image: $uri" >&2
exec ${pkgs.curl}/bin/curl -fsS -X DELETE "$uri"
;;
(*)
echo "don't know how to open URI: $uri" >&2
exit 1
esac
'';
};
})
];
}
// {
dev = pkgs.write "flameshot-once-tools" {
"/bin/get-buttonTypes" = {
executable = true;
text = /* sh */ ''
#! ${pkgs.dash}/bin/dash
indent=$(${placeholder "out"}/bin/indent-of buttonTypes)
src=${cfg.package.src}/src/tools/capturetool.h
${pkgs.coreutils}/bin/cat "$src" |
${pkgs.gnused}/bin/sed -nr '
s/^\s*(TYPE_\S+)\s*=\s*([0-9]+),/\1 = \2;/p
' |
${placeholder "out"}/bin/prefix " $indent"
'';
};
"/bin/get-iterableButtonTypes" = {
executable = true;
text = /* sh */ ''
#! ${pkgs.dash}/bin/dash
indent=$(${placeholder "out"}/bin/indent-of iterableButtonTypes)
src=${cfg.package.src}/src/widgets/capture/capturetoolbutton.cpp
${pkgs.coreutils}/bin/cat "$src" |
${pkgs.gnused}/bin/sed -n '/\<iterableButtonTypes = {/,/^}/p' |
${pkgs.gcc}/bin/cpp |
${pkgs.coreutils}/bin/tr , \\n |
${pkgs.gnused}/bin/sed -rn 's/^ *CaptureTool::(TYPE_[A-Z_]+).*/"\1"/p' |
${pkgs.coreutils}/bin/sort |
${placeholder "out"}/bin/prefix " $indent"
'';
};
"/bin/get-recognizedGeneralOptions" = {
executable = true;
text = /* sh */ ''
#! ${pkgs.dash}/bin/dash
src=${cfg.package.src}/src/utils/confighandler.cpp
${pkgs.coreutils}/bin/cat "$src" |
${pkgs.gnused}/bin/sed -n '/\<recognizedGeneralOptions = {/,/^};/p' |
${pkgs.gcc}/bin/cpp |
${pkgs.gnugrep}/bin/grep -F OPTION |
${pkgs.coreutils}/bin/sort
'';
};
"/bin/get-Shortcuts" = {
executable = true;
text = /* sh */ ''
#! ${pkgs.dash}/bin/dash
indent=$(${placeholder "out"}/bin/indent-of Shortcuts)
src=${cfg.package.src}/src/utils/confighandler.cpp
${pkgs.coreutils}/bin/cat "$src" |
${pkgs.gnused}/bin/sed -n '/recognizedShortcuts = {/,/^};/p ' |
${pkgs.gcc}/bin/cpp |
${pkgs.gnused}/bin/sed -nr 's/^\s*SHORTCUT\("(TYPE_[^"]+).*/"\1"/p' |
${pkgs.coreutils}/bin/sort |
${placeholder "out"}/bin/prefix " $indent"
'';
};
"/bin/indent-of" = {
executable = true;
text = /* sh */ ''
#! ${pkgs.dash}/bin/dash
# usage: indent-of NAME NIX_FILE
exec ${pkgs.gawk}/bin/awk -v name="$1" '
$1 == name && $2 == "=" {
sub("[^ ].*", "")
print
}
' ${./config.nix}
'';
};
"/bin/prefix" = {
executable = true;
text = /* sh */ ''
#! ${pkgs.dash}/bin/dash
${pkgs.gawk}/bin/awk -v prefix="$1" '{ print prefix $0 }'
'';
};
};
}

View File

@ -0,0 +1,16 @@
{ pkgs }:
pkgs.flameshot.overrideAttrs (old: rec {
name = "flameshot-${version}";
version = "12.1.0-pre";
src = pkgs.fetchFromGitHub {
owner = "flameshot-org";
repo = "flameshot";
rev = "f7e41f4d708e50eeaec892408069da25a28e04a2";
hash = "sha256-fZquXY0xSaN1hJgCh16MocIlvxHe1c2Nt+fGF2NIOVw=";
};
patches = old.patches or [] ++ [
./flameshot-12.imgur.patch
./flameshot-12.history.patch
];
})

View File

@ -0,0 +1,28 @@
diff --git a/src/utils/history.cpp b/src/utils/history.cpp
index f3ee09d0..7c85c34b 100644
--- a/src/utils/history.cpp
+++ b/src/utils/history.cpp
@@ -76,9 +76,9 @@ const HistoryFileName& History::unpackFileName(const QString& fileNamePacked)
int nPathIndex = fileNamePacked.lastIndexOf("/");
QStringList unpackedFileName;
if (nPathIndex == -1) {
- unpackedFileName = fileNamePacked.split("-");
+ unpackedFileName = fileNamePacked.split("|");
} else {
- unpackedFileName = fileNamePacked.mid(nPathIndex + 1).split("-");
+ unpackedFileName = fileNamePacked.mid(nPathIndex + 1).split("|");
}
switch (unpackedFileName.length()) {
@@ -109,9 +109,9 @@ const QString& History::packFileName(const QString& storageType,
if (storageType.length() > 0) {
if (deleteToken.length() > 0) {
m_packedFileName =
- storageType + "-" + deleteToken + "-" + m_packedFileName;
+ storageType + "|" + deleteToken + "|" + m_packedFileName;
} else {
- m_packedFileName = storageType + "-" + m_packedFileName;
+ m_packedFileName = storageType + "|" + m_packedFileName;
}
}
return m_packedFileName;

View File

@ -0,0 +1,43 @@
diff --git a/src/tools/imgupload/storages/imgur/imguruploader.cpp b/src/tools/imgupload/storages/imgur/imguruploader.cpp
index d6748b5a..5bb8d7de 100644
--- a/src/tools/imgupload/storages/imgur/imguruploader.cpp
+++ b/src/tools/imgupload/storages/imgur/imguruploader.cpp
@@ -16,6 +16,7 @@
#include <QNetworkRequest>
#include <QShortcut>
#include <QUrlQuery>
+#include <stdlib.h>
ImgurUploader::ImgurUploader(const QPixmap& capture, QWidget* parent)
: ImgUploaderBase(capture, parent)
@@ -70,7 +71,13 @@ void ImgurUploader::upload()
QString description = FileNameHandler().parsedPattern();
urlQuery.addQueryItem(QStringLiteral("description"), description);
- QUrl url(QStringLiteral("https://api.imgur.com/3/image"));
+ const char *IMGUR_CREATE_URL = secure_getenv("IMGUR_CREATE_URL");
+ QString createUrlPattern =
+ IMGUR_CREATE_URL != NULL
+ ? QString::fromUtf8(IMGUR_CREATE_URL)
+ : QStringLiteral("https://api.imgur.com/3/image")
+ ;
+ QUrl url(createUrlPattern);
url.setQuery(urlQuery);
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader,
@@ -87,8 +94,14 @@ void ImgurUploader::deleteImage(const QString& fileName,
const QString& deleteToken)
{
Q_UNUSED(fileName)
+ const char *IMGUR_DELETE_URL = secure_getenv("IMGUR_DELETE_URL");
+ QString deleteImageURLPattern =
+ IMGUR_DELETE_URL != NULL
+ ? QString::fromUtf8(IMGUR_DELETE_URL)
+ : QStringLiteral("https://imgur.com/delete/%1")
+ ;
bool successful = QDesktopServices::openUrl(
- QUrl(QStringLiteral("https://imgur.com/delete/%1").arg(deleteToken)));
+ QUrl(deleteImageURLPattern.arg(deleteToken)));
if (!successful) {
notification()->showMessage(tr("Unable to open the URL."));
}

View File

@ -1,7 +1,7 @@
{ attr, coreutils, exiv2, findutils, gnugrep, jq, nix, stockholm, util-linux, stdenv }: { attr, coreutils, exiv2, findutils, gnugrep, jq, nix, stockholm, util-linux, stdenv }:
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
pname = "htgen-imgur"; pname = "htgen-imgur";
version = "1.0.0"; version = "1.2.0";
src = ./src; src = ./src;

View File

@ -99,7 +99,7 @@ case "$Method $path" in
if item=$(find_item $base32short); then if item=$(find_item $base32short); then
deletehash=$(uuidgen) deletehash=$(uuidgen | tr -d -)
info=$( info=$(
exiv2 print "$item" | exiv2 print "$item" |

View File

@ -1,14 +1,17 @@
{ pkgs }: { pkgs }:
pkgs.writeDashBin "pager" '' pkgs.symlinkJoin {
name = "pager-wrapper";
paths = [
(pkgs.writeDashBin "pager" ''
# usage: pager {view,shift,shiftview} # usage: pager {view,shift,shiftview}
# #
# Environment variables # Environment variables
# #
# PAGER_NAME (default: Pager) # PAGER_NAME (default: Pager)
# The environment variables specifies the application name under which # The environment variables specifies the application name under
# resources are to be obtained. PAGER_NAME should not contain “.” or “*” # which resources are to be obtained. PAGER_NAME should not contain
# characters. # “.” or “*” characters.
# #
set -efu set -efu
@ -28,9 +31,12 @@ pkgs.writeDashBin "pager" ''
exec ${pkgs.xterm}/bin/xterm \ exec ${pkgs.xterm}/bin/xterm \
-name "$name" \ -name "$name" \
-ti vt340 \ -ti vt340 \
-xrm 'Pager*geometry: 32x10' \ -xrm '*geometry: 32x10' \
-xrm 'Pager*internalBorder: 2' \ -xrm '*internalBorder: 2' \
-xrm 'Pager*background: #050505' \ -xrm '*background: #050505' \
-xrm 'Pager*foreground: #d0d7d0' \ -xrm '*foreground: #d0d7d0' \
-e ${pkgs.haskellPackages.pager}/bin/pager "$@" -e ${pkgs.haskellPackages.pager}/bin/pager "$@"
'' '')
pkgs.haskellPackages.pager
];
}

View File

@ -1,15 +0,0 @@
{ writeDashBin, xdotool, xorg }:
writeDashBin "xwaitforwindow" ''
# usage: xwaitforwindow ARGS
# see xdotool search for possible ARGS
# example: xwaitforwindow -name WINDOWNAME
set -efu
if id=$(${xdotool}/bin/xdotool search "$@"); then
printf 'waiting for window %#x\n' "$id" >&2
exec ${xorg.xprop}/bin/xprop -spy -id "$id" >/dev/null
else
printf 'no window found with xdotool search %s\n' "$*" >&2
exit 1
fi
''

View File

@ -45,6 +45,8 @@ let
genid_uint31 = x: ((lib.genid_uint32 x) + 16777216) / 2; genid_uint31 = x: ((lib.genid_uint32 x) + 16777216) / 2;
genid_uint32 = import ./genid.nix { inherit lib; }; genid_uint32 = import ./genid.nix { inherit lib; };
hexchars = stringToCharacters "0123456789abcdef";
lpad = n: c: s: lpad = n: c: s:
if lib.stringLength s < n if lib.stringLength s < n
then lib.lpad n c (c + s) then lib.lpad n c (c + s)

View File

@ -32,6 +32,5 @@ let out = genid;
hexint = x: hexvals.${toLower x}; hexint = x: hexvals.${toLower x};
# :: attrset char uint4 # :: attrset char uint4
hexvals = listToAttrs (imap (i: c: { name = c; value = i - 1; }) hexvals = listToAttrs (imap (i: c: { name = c; value = i - 1; }) hexchars);
(stringToCharacters "0123456789abcdef"));
in out in out

149
lib/svg-colors.json Normal file
View File

@ -0,0 +1,149 @@
[
"aliceblue",
"antiquewhite",
"aqua",
"aquamarine",
"azure",
"beige",
"bisque",
"black",
"blanchedalmond",
"blue",
"blueviolet",
"brown",
"burlywood",
"cadetblue",
"chartreuse",
"chocolate",
"coral",
"cornflowerblue",
"cornsilk",
"crimson",
"cyan",
"darkblue",
"darkcyan",
"darkgoldenrod",
"darkgray",
"darkgreen",
"darkgrey",
"darkkhaki",
"darkmagenta",
"darkolivegreen",
"darkorange",
"darkorchid",
"darkred",
"darksalmon",
"darkseagreen",
"darkslateblue",
"darkslategray",
"darkslategrey",
"darkturquoise",
"darkviolet",
"deeppink",
"deepskyblue",
"dimgray",
"dimgrey",
"dodgerblue",
"firebrick",
"floralwhite",
"forestgreen",
"fuchsia",
"gainsboro",
"ghostwhite",
"gold",
"goldenrod",
"gray",
"green",
"greenyellow",
"grey",
"honeydew",
"hotpink",
"indianred",
"indigo",
"ivory",
"khaki",
"lavender",
"lavenderblush",
"lawngreen",
"lemonchiffon",
"lightblue",
"lightcoral",
"lightcyan",
"lightgoldenrodyellow",
"lightgray",
"lightgreen",
"lightgrey",
"lightpink",
"lightsalmon",
"lightseagreen",
"lightskyblue",
"lightslategray",
"lightslategrey",
"lightsteelblue",
"lightyellow",
"lime",
"limegreen",
"linen",
"magenta",
"maroon",
"mediumaquamarine",
"mediumblue",
"mediumorchid",
"mediumpurple",
"mediumseagreen",
"mediumslateblue",
"mediumspringgreen",
"mediumturquoise",
"mediumvioletred",
"midnightblue",
"mintcream",
"mistyrose",
"moccasin",
"navajowhite",
"navy",
"oldlace",
"olive",
"olivedrab",
"orange",
"orangered",
"orchid",
"palegoldenrod",
"palegreen",
"paleturquoise",
"palevioletred",
"papayawhip",
"peachpuff",
"peru",
"pink",
"plum",
"powderblue",
"purple",
"red",
"rosybrown",
"royalblue",
"saddlebrown",
"salmon",
"sandybrown",
"seagreen",
"seashell",
"sienna",
"silver",
"skyblue",
"slateblue",
"slategray",
"slategrey",
"snow",
"springgreen",
"steelblue",
"tan",
"teal",
"thistle",
"tomato",
"turquoise",
"violet",
"wheat",
"white",
"whitesmoke",
"yellow",
"yellowgreen"
]

View File

@ -3,11 +3,11 @@
let let
inherit (lib) inherit (lib)
all any attrNames concatMapStringsSep concatStringsSep const filter flip all any attrNames concatMapStringsSep concatStringsSep const filter flip
genid_uint31 hasSuffix head isInt isString length mergeOneOption mkOption genid_uint31 hasSuffix head importJSON isInt isString length mergeOneOption
mkOptionType optional optionalAttrs optionals range splitString mkOption mkOptionType optional optionalAttrs optionals range splitString
stringLength substring test testString typeOf; stringLength substring test testString typeOf;
inherit (lib.types) inherit (lib.types)
attrsOf bool either enum int lines listOf nullOr path str submodule; addCheck attrsOf bool either enum int lines listOf nullOr path str submodule;
in in
rec { rec {
@ -287,15 +287,27 @@ rec {
}; };
}); });
boundedInt = min: max: mkOptionType {
name = "bounded integer";
check = x: isInt x && min <= x && x <= max;
merge = mergeOneOption;
};
lowerBoundedInt = min: mkOptionType {
name = "lower bounded integer";
check = x: isInt x && min <= x;
merge = mergeOneOption;
};
positive = mkOptionType { positive = mkOptionType {
inherit (lowerBoundedInt 1) check;
name = "positive integer"; name = "positive integer";
check = x: isInt x && x > 0;
merge = mergeOneOption; merge = mergeOneOption;
}; };
uint = mkOptionType { uint = mkOptionType {
inherit (lowerBoundedInt 0) check;
name = "unsigned integer"; name = "unsigned integer";
check = x: isInt x && x >= 0;
merge = mergeOneOption; merge = mergeOneOption;
}; };
@ -583,6 +595,9 @@ rec {
}; };
}; };
flameshot.color =
either (addCheck str (test "#[0-9A-Fa-f]{6}")) svg.color-keyword;
file-mode = mkOptionType { file-mode = mkOptionType {
name = "file mode"; name = "file mode";
check = test "[0-7]{4}"; check = test "[0-7]{4}";
@ -601,6 +616,19 @@ rec {
merge = mergeOneOption; merge = mergeOneOption;
}; };
# SVG 1.1, 4.4 Recognized color keyword names
#
# svg-colors.json has been generated with:
# curl -sS https://www.w3.org/TR/SVG11/types.html#ColorKeywords |
# fq -d html '[
# grep_by(.["@class"]=="color-keywords") |
# grep_by(.["@class"]=="prop-value"and.["#text"]!="").["#text"]
# ] | sort'
#
svg.color-keyword = enum (importJSON ./svg-colors.json) // {
name = "SVG 1.1 recognized color keyword";
};
systemd.unit-name = mkOptionType { systemd.unit-name = mkOptionType {
name = "systemd unit name"; name = "systemd unit name";
check = x: check = x:

View File

@ -74,9 +74,6 @@ with import ./lib;
disko = { disko = {
cgit.desc = "declarative partitioning and formatting tool"; cgit.desc = "declarative partitioning and formatting tool";
}; };
flameshot-once = {
cgit.desc = "flameshot runner that automatically starts/stops the daemon";
};
fswm = { fswm = {
cgit.desc = "simple full screen window manager"; cgit.desc = "simple full screen window manager";
}; };
@ -139,6 +136,9 @@ with import ./lib;
cgserver = {}; cgserver = {};
crude-mail-setup = {}; crude-mail-setup = {};
dot-xmonad = {}; dot-xmonad = {};
flameshot-once = {
cgit.desc = "flameshot runner that automatically starts/stops the daemon";
};
hirc = {}; hirc = {};
hstool = { hstool = {
cgit.desc = "Haskell Development Environment ^_^"; cgit.desc = "Haskell Development Environment ^_^";

View File

@ -120,13 +120,7 @@ in {
}; };
path = [ path = [
config.tv.slock.package config.tv.slock.package
(pkgs.flameshot-once.override { pkgs.flameshot-once-tv
config.imgur.enable = true;
config.imgur.createUrl = "http://ni.r/image";
config.imgur.deleteUrl = "http://ni.r/image/delete/%1";
config.imgur.xdg-open.browser = "/etc/profiles/per-user/tv/bin/cr";
config.timeout = 200;
})
pkgs.pulseaudio.out pkgs.pulseaudio.out
pkgs.rxvt_unicode pkgs.rxvt_unicode
pkgs.xcalib pkgs.xcalib

View File

@ -1,20 +0,0 @@
{ mkDerivation, async, base, blessings, bytestring, dbus, fetchgit
, iso8601-time, lib, process, random, text, time, unagi-chan, unix
}:
mkDerivation {
pname = "flameshot-once";
version = "1.4.0";
src = fetchgit {
url = "https://cgit.krebsco.de/flameshot-once";
sha256 = "13szgsiwn29aixm5xvs1m7128y5km5xss0ry5ii5y068rc2vysw8";
rev = "4475893c2081b3d9db4b7a54d0ce38d0914a17bf";
fetchSubmodules = true;
};
isLibrary = false;
isExecutable = true;
executableHaskellDepends = [
async base blessings bytestring dbus iso8601-time process random
text time unagi-chan unix
];
license = lib.licenses.mit;
}

View File

@ -1,6 +1,5 @@
{ mkDerivation, aeson, base, bytestring, containers, directory { mkDerivation, aeson, base, bytestring, containers, directory
, extra, filepath, lib, systemd, template-haskell, th-env , extra, filepath, lib, pager, unix, X11, xmonad, xmonad-contrib
, transformers, unix, X11, xmonad, xmonad-contrib
}: }:
mkDerivation { mkDerivation {
pname = "xmonad-tv"; pname = "xmonad-tv";
@ -9,8 +8,9 @@ mkDerivation {
isLibrary = false; isLibrary = false;
isExecutable = true; isExecutable = true;
executableHaskellDepends = [ executableHaskellDepends = [
aeson base bytestring containers directory extra filepath systemd aeson base bytestring containers directory extra filepath pager
template-haskell th-env transformers unix X11 xmonad xmonad-contrib unix X11 xmonad xmonad-contrib
]; ];
license = lib.licenses.mit; license = lib.licenses.mit;
mainProgram = "xmonad";
} }

View File

@ -0,0 +1,117 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE NamedFieldPuns #-}
module XMonad.Hooks.EwmhDesktops.Extra where
import Control.Monad (when)
import Data.Maybe (fromMaybe)
import Data.Monoid (All)
import Data.Tuple.Extra (both)
import Graphics.X11.EWMH (getDesktopNames, setDesktopNames)
import Graphics.X11.EWMH.Atom (_NET_DESKTOP_NAMES)
import Graphics.X11.Xlib.Display.Extra (withDefaultDisplay)
import XMonad hiding (workspaces)
import XMonad.Actions.DynamicWorkspaces (addHiddenWorkspace, removeEmptyWorkspaceByTag)
import XMonad.StackSet (mapWorkspace, tag, workspaces)
import XMonad.Util.WorkspaceCompare (getSortByIndex)
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import qualified XMonad
ewmhExtra :: XConfig a -> IO (XConfig a)
ewmhExtra c = do
-- XMonad.Hooks.EwmhDesktops.setDesktopViewport uses _NET_DESKTOP_VIEWPORT
-- only if it exists. This seems to be a harmless issue, but by creating
-- the atom here, we suppress the error message:
--
-- xmonad: X11 error: BadAtom (invalid Atom parameter),
-- request code=18, error code=5
--
_ <-
withDefaultDisplay $ \dpy -> internAtom dpy "_NET_DESKTOP_VIEWPORT" False
initialWorkspaces <-
Data.Maybe.fromMaybe (XMonad.workspaces def)
<$> withDefaultDisplay getDesktopNames
return
c { handleEventHook = ewmhDesktopsExtraEventHook <> handleEventHook c
, rootMask = rootMask c .|. propertyChangeMask
, XMonad.workspaces = initialWorkspaces
}
ewmhDesktopsExtraEventHook :: Event -> X All
ewmhDesktopsExtraEventHook = \case
PropertyEvent{ev_window, ev_atom} -> do
r <- asks theRoot
when (ev_window == r && ev_atom == _NET_DESKTOP_NAMES) $
withDisplay $ \dpy -> do
sort <- getSortByIndex
oldNames <- gets $ map tag . sort . workspaces . windowset
newNames <- fromMaybe oldNames <$> io (getDesktopNames dpy)
let
(renamesFrom, renamesTo) = both Set.fromList $ unzip renames
renames = go oldNames newNames where
go old@(headOld : tailOld) new@(headNew : tailNew) = do
let
deleteOld = Set.member headOld deleteNameSet
createNew = Set.member headNew createNameSet
if
| headOld == headNew ->
-- assert (not deleteOld && not createNew)
go tailOld tailNew
| deleteOld && createNew ->
(headOld, headNew) :
go tailOld tailNew
| deleteOld ->
go tailOld new
| createNew ->
go old tailNew
| otherwise ->
-- assert (headOld == headNew)
go tailOld tailNew
go _ _ = []
oldNameSet = Set.fromList oldNames
newNameSet = Set.fromList newNames
deleteNameSet = Set.difference oldNameSet newNameSet
createNameSet = Set.difference newNameSet oldNameSet
deleteNames = Set.toAscList $
Set.difference deleteNameSet renamesFrom
createNames = Set.toAscList $
Set.difference createNameSet renamesTo
mapM_ addHiddenWorkspace createNames
mapM_ removeEmptyWorkspaceByTag deleteNames
when (not (null renames)) $ do
let
renameMap = Map.fromList renames
rename w =
case Map.lookup (tag w) renameMap of
Just newName -> w { tag = newName }
Nothing -> w
modifyWindowSet $ mapWorkspace rename
names <- gets $ map tag . sort . workspaces . windowset
when (names /= newNames) $ do
trace $ "setDesktopNames " <> show names
io (setDesktopNames names dpy)
mempty
_ ->
mempty

View File

@ -5,16 +5,15 @@ module Main (main) where
import System.Exit (exitFailure) import System.Exit (exitFailure)
import XMonad.Hooks.EwmhDesktops (ewmh) import XMonad.Hooks.EwmhDesktops (ewmh)
import XMonad.Hooks.EwmhDesktops.Extra (ewmhExtra)
import XMonad.Hooks.RefocusLast (refocusLastLayoutHook, toggleFocus) import XMonad.Hooks.RefocusLast (refocusLastLayoutHook, toggleFocus)
import Control.Exception
import Control.Monad.Extra (whenJustM) import Control.Monad.Extra (whenJustM)
import qualified Data.Aeson import qualified Data.Aeson
import qualified Data.ByteString.Char8 import qualified Data.ByteString.Char8
import qualified Data.List import qualified Data.List
import qualified Data.Maybe import qualified Data.Maybe
import Graphics.X11.ExtraTypes.XF86 import Graphics.X11.ExtraTypes.XF86
import Text.Read (readEither)
import XMonad import XMonad
import XMonad.Extra (isFloatingX) import XMonad.Extra (isFloatingX)
import System.IO (hPutStrLn, stderr) import System.IO (hPutStrLn, stderr)
@ -76,11 +75,10 @@ mainNoArgs = do
myTermFont <- getEnv "XMONAD_TERM_FONT" myTermFont <- getEnv "XMONAD_TERM_FONT"
myTermFontWidth <- readEnv "XMONAD_TERM_FONT_WIDTH" :: IO Dimension myTermFontWidth <- readEnv "XMONAD_TERM_FONT_WIDTH" :: IO Dimension
myTermPadding <- readEnv "XMONAD_TERM_PADDING" :: IO Dimension myTermPadding <- readEnv "XMONAD_TERM_PADDING" :: IO Dimension
workspaces0 <- getWorkspaces0
handleShutdownEvent <- newShutdownEventHandler handleShutdownEvent <- newShutdownEventHandler
let config <-
config = ewmhExtra
ewmh $ ewmh
$ withUrgencyHookC $ withUrgencyHookC
BorderUrgencyHook BorderUrgencyHook
{ urgencyBorderColor = "#ff0000" { urgencyBorderColor = "#ff0000"
@ -93,7 +91,6 @@ mainNoArgs = do
{ terminal = {-pkg:alacritty-tv-}"alacritty" { terminal = {-pkg:alacritty-tv-}"alacritty"
, modMask = mod4Mask , modMask = mod4Mask
, keys = myKeys myTermFont , keys = myKeys myTermFont
, workspaces = workspaces0
, layoutHook = , layoutHook =
refocusLastLayoutHook $ refocusLastLayoutHook $
gaps (zip [U,R,D,L] myScreenGaps) $ gaps (zip [U,R,D,L] myScreenGaps) $
@ -125,23 +122,6 @@ mainNoArgs = do
launch config directories launch config directories
getWorkspaces0 :: IO [String]
getWorkspaces0 =
try (getEnv "XMONAD_WORKSPACES0_FILE") >>= \case
Left e -> warn (displaySomeException e)
Right p -> try (readFile p) >>= \case
Left e -> warn (displaySomeException e)
Right x -> case readEither x of
Left e -> warn e
Right y -> return y
where
warn msg = hPutStrLn stderr ("getWorkspaces0: " ++ msg) >> return []
displaySomeException :: SomeException -> String
displaySomeException = displayException
forkFile :: FilePath -> [String] -> Maybe [(String, String)] -> X () forkFile :: FilePath -> [String] -> Maybe [(String, String)] -> X ()
forkFile path args env = forkFile path args env =
xfork (executeFile path True args env) >> return () xfork (executeFile path True args env) >> return ()
@ -206,7 +186,7 @@ myKeys font conf = Map.fromList $
, ((_4, xK_Prior), forkFile {-pkg-}"xcalib" ["-invert", "-alter"] Nothing) , ((_4, xK_Prior), forkFile {-pkg-}"xcalib" ["-invert", "-alter"] Nothing)
, ((0, xK_Print), forkFile {-pkg-}"flameshot" [] Nothing) , ((0, xK_Print), forkFile {-pkg:flameshot-once-tv-}"flameshot-once" [] Nothing)
, ((_C, xF86XK_Forward), forkFile {-pkg:xdpytools-}"xdpychvt" ["next"] Nothing) , ((_C, xF86XK_Forward), forkFile {-pkg:xdpytools-}"xdpychvt" ["next"] Nothing)
, ((_C, xF86XK_Back), forkFile {-pkg:xdpytools-}"xdpychvt" ["prev"] Nothing) , ((_C, xF86XK_Back), forkFile {-pkg:xdpytools-}"xdpychvt" ["prev"] Nothing)

View File

@ -9,20 +9,21 @@ cabal-version: >=1.10
executable xmonad executable xmonad
main-is: main.hs main-is: main.hs
build-depends: build-depends:
aeson, base
base, , X11
bytestring, , aeson
containers, , bytestring
directory, , containers
extra, , directory
filepath, , extra
template-haskell, , filepath
th-env, , pager
unix, , unix
X11, , xmonad
xmonad, , xmonad-contrib
xmonad-contrib
other-modules: other-modules:
Shutdown Shutdown
XMonad.Extra
XMonad.Hooks.EwmhDesktops.Extra
default-language: Haskell2010 default-language: Haskell2010
ghc-options: -O2 -Wall -threaded ghc-options: -O2 -Wall

View File

@ -1,15 +0,0 @@
self: super:
super.flameshot.overrideAttrs (old: rec {
name = "flameshot-${version}";
version = "0.10.2";
src = self.fetchFromGitHub {
owner = "flameshot-org";
repo = "flameshot";
rev = "v${version}";
sha256 = "sha256-rZUiaS32C77tFJmEkw/9MGbVTVscb6LOCyWaWO5FyR4=";
};
patches = old.patches or [] ++ [
./flameshot_imgur_0.10.2.patch
];
})

View File

@ -1,35 +0,0 @@
--- a/src/tools/imgur/imguruploader.cpp
+++ b/src/tools/imgur/imguruploader.cpp
@@ -31,6 +31,7 @@
#include <QTimer>
#include <QUrlQuery>
#include <QVBoxLayout>
+#include <stdlib.h>
ImgurUploader::ImgurUploader(const QPixmap& capture, QWidget* parent)
: QWidget(parent)
@@ -79,8 +80,11 @@ void ImgurUploader::handleReply(QNetworkReply* reply)
m_imageURL.setUrl(data[QStringLiteral("link")].toString());
auto deleteToken = data[QStringLiteral("deletehash")].toString();
+ char *deleteImageURLPattern = secure_getenv("IMGUR_DELETE_URL");
+ if (deleteImageURLPattern == NULL)
+ deleteImageURLPattern = "https://imgur.com/delete/%1";
m_deleteImageURL.setUrl(
- QStringLiteral("https://imgur.com/delete/%1").arg(deleteToken));
+ QString::fromUtf8(deleteImageURLPattern).arg(deleteToken));
// save history
QString imageName = m_imageURL.toString();
@@ -133,7 +137,10 @@ void ImgurUploader::upload()
QString description = FileNameHandler().parsedPattern();
urlQuery.addQueryItem(QStringLiteral("description"), description);
- QUrl url(QStringLiteral("https://api.imgur.com/3/image"));
+ char *createImageURLPattern = secure_getenv("IMGUR_CREATE_URL");
+ if (createImageURLPattern == NULL)
+ createImageURLPattern = "https://api.imgur.com/3/image";
+ QUrl url(QString::fromUtf8(createImageURLPattern));
url.setQuery(urlQuery);
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader,

View File

@ -0,0 +1,48 @@
{ pkgs }:
pkgs.flameshot-once.override {
name = "flameshot-once-tv";
config.imgur.enable = true;
config.imgur.createUrl = "http://ni.r/image";
config.imgur.deleteUrl = "http://ni.r/image/delete/%1";
config.imgur.xdg-open.browser = "/etc/profiles/per-user/tv/bin/cr";
config.settings.General = {
autoCloseIdleDaemon = true;
buttons = [
"TYPE_ARROW"
"TYPE_CIRCLE"
"TYPE_CIRCLECOUNT"
"TYPE_COPY"
"TYPE_DRAWER"
"TYPE_IMAGEUPLOADER"
"TYPE_MARKER"
"TYPE_MOVESELECTION"
"TYPE_PENCIL"
"TYPE_PIXELATE"
"TYPE_RECTANGLE"
"TYPE_SAVE"
"TYPE_SELECTION"
"TYPE_TEXT"
];
checkForUpdates = false;
contrastOpacity = 220;
copyPathAfterSave = true;
disabledTrayIcon = true;
drawColor = "#E4002B";
drawThickness = 8;
filenamePattern = "%FT%T%z_flameshot";
fontFamily = "iosevka tv 2";
savePath = "/tmp";
savePathFixed = true;
showDesktopNotification = false;
showHelp = false;
showSidePanelButton = false;
showStartupLaunchMessage = false;
squareMagnifier = true;
uploadWithoutConfirmation = true;
};
config.settings.Shortcuts = {
TYPE_COPY = "Return";
TYPE_TOGGLE_PANEL = "`";
};
}

View File

@ -1,28 +0,0 @@
{ pkgs, stockholm, ... }@args:
with stockholm.lib;
let
# config cannot be declared in the input attribute set because that would
# cause callPackage to inject the wrong config. Instead, get it from ...
# via args.
config = args.config or {};
in
pkgs.symlinkJoin {
name = "flameshot-once-wrapper";
paths = [
(pkgs.writeDashBin "flameshot-once" ''
export PATH=${makeBinPath [
pkgs.flameshot
pkgs.qt5.qtbase
pkgs.xclip
pkgs.xwaitforwindow
]}
${optionalString (config != null) /* sh */ ''
. ${import ./profile.nix { inherit config pkgs; }}
''}
exec ${pkgs.haskellPackages.flameshot-once}/bin/flameshot-once "$@"
'')
pkgs.haskellPackages.flameshot-once
];
}

View File

@ -1,235 +0,0 @@
{ config, pkgs }:
with pkgs.stockholm.lib;
with generators;
let
# Refs https://github.com/lupoDharkael/flameshot/blob/master/src/widgets/capture/capturebutton.h
ButtonType = {
PENCIL = 0;
DRAWER = 1;
ARROW = 2;
SELECTION = 3;
RECTANGLE = 4;
CIRCLE = 5;
MARKER = 6;
SELECTIONINDICATOR = 7;
MOVESELECTION = 8;
UNDO = 9;
COPY = 10;
SAVE = 11;
EXIT = 12;
IMAGEUPLOADER = 13;
OPEN_APP = 14;
BLUR = 15;
REDO = 16;
PIN = 17;
TEXT = 18;
CIRCLECOUNT = 19;
};
cfg = eval.config;
eval = evalModules {
modules = singleton {
_file = toString ./profile.nix;
imports = singleton config;
options = {
buttons = mkOption {
apply = map (name: ButtonType.${name});
default = [
"PENCIL"
"DRAWER"
"ARROW"
"SELECTION"
"RECTANGLE"
"CIRCLE"
"MARKER"
"SELECTIONINDICATOR"
"MOVESELECTION"
"UNDO"
"SAVE"
"EXIT"
"BLUR"
"CIRCLECOUNT"
]
++ optional cfg.imgur.enable "IMAGEUPLOADER"
;
type = types.listOf (types.enum (attrNames ButtonType));
};
copyAndCloseAfterUpload = mkOption {
default = false;
type = types.bool;
};
disabledTrayIcon = mkOption {
default = true;
type = types.bool;
};
drawColor = mkOption {
default = "#ff0000";
type =
types.addCheck types.str (test "#[0-9A-Fa-f]{6}");
};
drawThickness = mkOption {
default = 8;
type = types.positive;
};
filenamePattern = mkOption {
default = "%FT%T%z_flameshot";
type =
# This is types.filename extended by [%:][%:+]*
types.addCheck types.str (test "[%:0-9A-Za-z._][%:+0-9A-Za-z._-]*");
};
imgur = mkOption {
default = {};
type = types.submodule {
options = {
enable = mkEnableOption "imgur";
createUrl = mkOption {
example = "http://p.r/image";
type = types.str;
};
deleteUrl = mkOption {
example = "http://p.r/image/delete/%1";
type = types.str;
};
xdg-open = mkOption {
default = {};
type = types.submodule {
options = {
enable = mkEnableOption "imgur.xdg-open" // {
default = true;
};
browser = mkOption {
default = "${pkgs.coreutils}/bin/false";
type = types.str;
};
createPrefix = mkOption {
default = cfg.imgur.createUrl;
type = types.str;
};
deletePrefix = mkOption {
default = removeSuffix "/%1" cfg.imgur.deleteUrl;
type = types.str;
};
};
};
};
};
};
};
savePath = mkOption {
default = "/tmp";
type = types.absolute-pathname;
};
showDesktopNotification = mkOption {
default = false;
type = types.bool;
};
showHelp = mkOption {
default = false;
type = types.bool;
};
showSidePanelButton = mkOption {
default = false;
type = types.bool;
};
showStartupLaunchMessage = mkOption {
default = false;
type = types.bool;
};
timeout = mkOption {
default = 200;
description = ''
Maximum time in milliseconds allowed for the flameshot daemon to
react.
'';
type = types.positive;
};
};
};
};
hexchars = stringToCharacters "0123456789abcdef";
# Encode integer to C-escaped string of bytes, little endian / LSB 0
le = rec {
x1 = i: let
i0 = mod i 16;
i1 = i / 16;
in
"\\x${elemAt hexchars i1}${elemAt hexchars i0}";
x2 = i: let
i0 = mod i 256;
i1 = i / 256;
in
"${x1 i0}${x1 i1}";
x4 = i: let
i0 = mod i 65536;
i1 = i / 65536;
in
"${x2 i0}${x2 i1}";
};
toQList = t: xs:
assert t == "int";
"QList<${t}>${le.x4 0}${le.x4 (length xs)}${concatMapStrings le.x4 xs}";
XDG_CONFIG_HOME = pkgs.write "flameshot-config" {
"/flameshot/flameshot.ini".text =
toINI {} {
General = {
buttons = ''@Variant(\0\0\0\x7f\0\0\0\v${toQList "int" cfg.buttons})'';
disabledTrayIcon = cfg.disabledTrayIcon;
checkForUpdates = false;
copyAndCloseAfterUpload = cfg.copyAndCloseAfterUpload;
drawColor = cfg.drawColor;
drawThickness = cfg.drawThickness;
filenamePattern = cfg.filenamePattern;
savePath = cfg.savePath;
showDesktopNotification = cfg.showDesktopNotification;
showHelp = cfg.showHelp;
showSidePanelButton = cfg.showSidePanelButton;
showStartupLaunchMessage = cfg.showStartupLaunchMessage;
startupLaunch = false;
};
Shortcuts = {
TYPE_COPY = "Return";
};
};
};
in
pkgs.writeDash "flameshot.profile" ''
export FLAMESHOT_CAPTURE_PATH=${cfg.savePath}
export FLAMESHOT_ONCE_TIMEOUT=${toString cfg.timeout}
export XDG_CONFIG_HOME=${XDG_CONFIG_HOME}
${optionalString cfg.imgur.enable /* sh */ ''
export IMGUR_CREATE_URL=${shell.escape cfg.imgur.createUrl}
export IMGUR_DELETE_URL=${shell.escape cfg.imgur.deleteUrl}
${optionalString cfg.imgur.xdg-open.enable /* sh */ ''
PATH=$PATH:${makeBinPath [
(pkgs.writeDashBin "xdg-open" ''
set -efu
uri=$1
prefix=$(${pkgs.coreutils}/bin/dirname "$uri")
case $prefix in
(${shell.escape cfg.imgur.xdg-open.createPrefix})
echo "opening image in browser: $uri" >&2
exec ${config.imgur.xdg-open.browser} "$uri"
;;
(${shell.escape cfg.imgur.xdg-open.deletePrefix})
echo "deleting image: $uri" >&2
exec ${pkgs.curl}/bin/curl -fsS -X DELETE "$uri"
;;
(*)
echo "don't know how to open URI: $uri" >&2
exit 1
esac
'')
]}
''}
''}
''