diff options
Diffstat (limited to 'pkgs')
39 files changed, 1875 insertions, 284 deletions
diff --git a/pkgs/aszlig/default.nix b/pkgs/aszlig/default.nix index 70c6ff9d..d1dbcb73 100644 --- a/pkgs/aszlig/default.nix +++ b/pkgs/aszlig/default.nix @@ -8,8 +8,8 @@ grandpa = callPackage ./grandpa { }; librxtx_java = callPackage ./librxtx-java { }; lockdev = callPackage ./lockdev { }; + psi = callPackage ./psi { }; pvolctrl = callPackage ./pvolctrl { }; - santander = callPackage_i686 ./santander { }; vim = callPackage ./vim { vim = vim_configurable; }; xournal = callPackage ./xournal { inherit xournal; }; } diff --git a/pkgs/aszlig/psi/config.patch b/pkgs/aszlig/psi/config.patch new file mode 100644 index 00000000..a9462102 --- /dev/null +++ b/pkgs/aszlig/psi/config.patch @@ -0,0 +1,278 @@ +diff --git a/options/default.xml b/options/default.xml +index 90f019b3..ef33997b 100644 +--- a/options/default.xml ++++ b/options/default.xml +@@ -19,7 +19,7 @@ + <domain comment="Always use the same domain to register with. Leave this empty to allow the user to choose his server." type="QString"/> + </account> + <auto-update comment="Auto updater"> +- <check-on-startup comment="Check for available updates on startup" type="bool">true</check-on-startup> ++ <check-on-startup comment="Check for available updates on startup" type="bool">false</check-on-startup> + </auto-update> + <enable-multicast comment="Enable multicasting messages to multiple recipients" type="bool">false</enable-multicast> + <html comment="Hypertext markup options"> +@@ -86,7 +86,7 @@ + <security comment="Options related to the seciruty UI"> + <show comment="Show the security UI" type="bool">true</show> + </security> +- <single comment="Limit the client to a single account" type="bool">false</single> ++ <single comment="Limit the client to a single account" type="bool">true</single> + </account> + <message comment="Message options"> + <enabled comment="Enable message (i.e. non-chat) functionality" type="bool">true</enabled> +@@ -128,7 +128,7 @@ + <default-jid-mode comment="Default jid mode: barejid | auto" type="QString">auto</default-jid-mode> + <default-jid-mode-ignorelist comment="Default autojid mode ignore list: jid1,jid2,..." type="QString"></default-jid-mode-ignorelist> + <history comment="Message history options"> +- <preload-history-size comment="The number of preloaded messages" type="int">5</preload-history-size> ++ <preload-history-size comment="The number of preloaded messages" type="int">10</preload-history-size> + </history> + </chat> + <save> +@@ -146,8 +146,8 @@ + <auto-delete-unlisted comment="Automatically remove an unlisted contact from the contact list if it does not have any pending messages anymore" type="bool">false</auto-delete-unlisted> + <opacity comment="Opacity percentage of the contact list" type="int">100</opacity> + <status-messages comment="Status messages for contacts"> +- <single-line comment="Show status messages on the same line as the nickname" type="bool">true</single-line> +- <show comment="Show status messages" type="bool">false</show> ++ <single-line comment="Show status messages on the same line as the nickname" type="bool">false</single-line> ++ <show comment="Show status messages" type="bool">true</show> + </status-messages> + <tooltip comment="Display options for the contact list tooltips"> + <css type="QString"></css> +@@ -208,7 +208,7 @@ QLineEdit#le_status_text { + <always-on-top type="bool">false</always-on-top> + <automatically-resize-roster type="bool">false</automatically-resize-roster> + <grow-roster-upwards type="bool">true</grow-roster-upwards> +- <disable-scrollbar type="bool">true</disable-scrollbar> ++ <disable-scrollbar type="bool">false</disable-scrollbar> + <contact-sort-style type="QString">status</contact-sort-style> + <disable-service-discovery type="bool">false</disable-service-discovery> + <enable-groups type="bool">true</enable-groups> +@@ -223,7 +223,7 @@ QLineEdit#le_status_text { + <agent-contacts type="bool">true</agent-contacts> + <away-contacts type="bool">true</away-contacts> + <hidden-contacts-group type="bool">true</hidden-contacts-group> +- <offline-contacts type="bool">true</offline-contacts> ++ <offline-contacts type="bool">false</offline-contacts> + <self-contact type="bool">true</self-contact> + </show> + <show-group-counts type="bool">true</show-group-counts> +@@ -248,7 +248,7 @@ QLineEdit#le_status_text { + <use-left-click type="bool">false</use-left-click> + <use-single-click type="bool">false</use-single-click> + <use-status-change-animation type="bool">true</use-status-change-animation> +- <aio-left-roster type="bool">false</aio-left-roster> ++ <aio-left-roster type="bool">true</aio-left-roster> + <use-transport-icons type="bool">true</use-transport-icons> + <saved-window-geometry type="QRect" > + <x>64</x> +@@ -285,7 +285,7 @@ QLineEdit#le_status_text { + <custom-pgp-key comment="Show the 'assign pgp key' menu" type="bool">true</custom-pgp-key> + </contact> + <main comment="Options for the main menu"> +- <change-profile comment="Show the 'change profile' menu" type="bool">true</change-profile> ++ <change-profile comment="Show the 'change profile' menu" type="bool">false</change-profile> + </main> + <status comment="Options for the status menu"> + <chat comment="Enable free for chat" type="bool">true</chat> +@@ -335,7 +335,7 @@ QLineEdit#le_status_text { + <disable-send-button type="bool">true</disable-send-button> + <systemtray comment="Options related to the system tray"> + <use-old comment="Use the old system tray code (deprecated)" type="bool">false</use-old> +- <enable type="bool">true</enable> ++ <enable type="bool">false</enable> + <use-double-click type="bool">false</use-double-click> + </systemtray> + <flash-windows comment="Allow windows to flash upon activity" type="bool">true</flash-windows> +@@ -353,8 +353,8 @@ QLineEdit#le_status_text { + <contactlist> + <background type="QColor"/> + <grouping> +- <header-background type="QColor">#f0f0f0</header-background> +- <header-foreground type="QColor">#5a5a5a</header-foreground> ++ <header-background type="QColor">#00007f</header-background> ++ <header-foreground type="QColor">#969696</header-foreground> + </grouping> + <profile> + <header-background type="QColor">#969696</header-background> +@@ -364,16 +364,16 @@ QLineEdit#le_status_text { + <away type="QColor">#004bb4</away> + <do-not-disturb type="QColor">#7e0000</do-not-disturb> + <offline type="QColor">#646464</offline> +- <online type="QColor"/> ++ <online type="QColor">#ffffff</online> + </status> +- <status-change-animation1 type="QColor">#000000</status-change-animation1> ++ <status-change-animation1 type="QColor">#6f0000</status-change-animation1> + <status-change-animation2 type="QColor">#969696</status-change-animation2> + <status-messages type="QColor">#808080</status-messages> + </contactlist> + <tooltip> + <enable comment="Enable tooltip coloring feature" type="bool">true</enable> + <background comment="Tooltip background color" type="QColor">#e9ecc7</background> +- <text comment="Tooltip text color" type="QColor">#000000</text> ++ <text comment="Tooltip text color" type="QColor">#ffffff</text> + </tooltip> + <muc> + <nick-colors type="QStringList" > +@@ -384,21 +384,21 @@ QLineEdit#le_status_text { + <item>Red</item> + </nick-colors> + <role-moderator type="QColor">#910000</role-moderator> +- <role-participant type="QColor">#00008a</role-participant> ++ <role-participant type="QColor">#00aaff</role-participant> + <role-visitor type="QColor">#336600</role-visitor> +- <role-norole type="QColor">black</role-norole> ++ <role-norole type="QColor">#cccccc</role-norole> + </muc> + <messages comment="Message coloring."> +- <received type="QColor" comment="Color used to indicate received messages.">#0000ff</received> ++ <received type="QColor" comment="Color used to indicate received messages.">#0055ff</received> + <sent type="QColor" comment="Color used to indicate sent messages.">#ff0000</sent> + <informational type="QColor" comment="Color used to indicate informational (status change, spooled) messages.">#008000</informational> + <usertext type="QColor" comment="Color used to indicate additional text for informational messages.">#606060</usertext> + <highlighting type="QColor">#FF0000</highlighting> +- <link type="QColor">#000080</link> ++ <link type="QColor">#55ffff</link> + <link-visited type="QColor">#400080</link-visited> + </messages> + <chat> +- <composing-color type="QColor">darkGreen</composing-color> ++ <composing-color type="QColor">#cccccc</composing-color> + <unread-message-color type="QColor">red</unread-message-color> + <inactive-color type="QColor">grey</inactive-color> + </chat> +@@ -411,10 +411,10 @@ QLineEdit#le_status_text { + <use-slim-group-headings type="bool">false</use-slim-group-headings> + </contactlist> + <font> +- <chat type="QString">Sans Serif,11,-1,5,50,0,0,0,0,0</chat> +- <contactlist type="QString">Sans Serif,11,-1,5,50,0,0,0,0,0</contactlist> +- <message type="QString">Sans Serif,11,-1,5,50,0,0,0,0,0</message> +- <passive-popup type="QString">Sans Serif,9,-1,5,50,0,0,0,0,0</passive-popup> ++ <chat type="QString">Monospace,12,-1,5,50,0,0,0,0,0</chat> ++ <contactlist type="QString">Monospace,12,-1,5,50,0,0,0,0,0</contactlist> ++ <message type="QString">Monospace,12,-1,5,50,0,0,0,0,0</message> ++ <passive-popup type="QString">Monospace,12,-1,5,50,0,0,0,0,0</passive-popup> + </font> + <css type="QString" /> + </look> +@@ -462,20 +462,20 @@ QLineEdit#le_status_text { + <suppress-while-away type="bool">false</suppress-while-away> + </popup-dialogs> + <sounds> +- <chat-message type="QString">sound/chat2.wav</chat-message> +- <groupchat-message type="QString">sound/chat2.wav</groupchat-message> +- <completed-file-transfer type="QString">sound/ft_complete.wav</completed-file-transfer> +- <contact-offline type="QString">sound/offline.wav</contact-offline> +- <contact-online type="QString">sound/online.wav</contact-online> +- <enable type="bool">true</enable> +- <incoming-file-transfer type="QString">sound/ft_incoming.wav</incoming-file-transfer> +- <incoming-headline type="QString">sound/chat2.wav</incoming-headline> +- <incoming-message type="QString">sound/chat2.wav</incoming-message> +- <new-chat type="QString">sound/chat1.wav</new-chat> ++ <chat-message type="QString"/> ++ <groupchat-message type="QString"/> ++ <completed-file-transfer type="QString"/> ++ <contact-offline type="QString"/> ++ <contact-online type="QString"/> ++ <enable type="bool">false</enable> ++ <incoming-file-transfer type="QString"/> ++ <incoming-headline type="QString"/> ++ <incoming-message type="QString"/> ++ <new-chat type="QString"/> + <notify-every-muc-message type="bool">false</notify-every-muc-message> +- <outgoing-chat type="QString">sound/send.wav</outgoing-chat> +- <silent-while-away type="bool">false</silent-while-away> +- <system-message type="QString">sound/chat2.wav</system-message> ++ <outgoing-chat type="QString"/> ++ <silent-while-away type="bool">true</silent-while-away> ++ <system-message type="QString"/> + <unix-sound-player type="QString"/> + </sounds> + <successful-subscription type="bool">false</successful-subscription> +@@ -494,7 +494,7 @@ QLineEdit#le_status_text { + <mouse-middle-button type="QString">close</mouse-middle-button> <!-- hide|close|detach --> + <mouse-doubleclick-action type="QString">detach</mouse-doubleclick-action> + <size type="QString"></size> <!-- will be invalid when converted to QSize so we can detect first load --> +- <grouping type="QString" comment="A ':' seperated list of groups of kinds of tabs to keep in the same tabset. 'C' for chat and 'M' for mucs. 'A' means using all in one window patch.">CM</grouping> ++ <grouping type="QString" comment="A ':' seperated list of groups of kinds of tabs to keep in the same tabset. 'C' for chat and 'M' for mucs. 'A' means using all in one window patch.">ACM</grouping> + <group-state comment="Saved state data of the tabsets defined by options.ui.tabs.grouping"/> + <tab-singles type="QString" comment="Tab types that would have been untabbed are given their own tabset. 'C' for chat and 'M' for mucs"/> + <use-tab-shortcuts type="bool">true</use-tab-shortcuts> +@@ -627,7 +627,7 @@ QLineEdit#le_status_text { + <last-activity type="bool">true</last-activity> + </service-discovery> + <status> +- <ask-for-message-on-offline type="bool">false</ask-for-message-on-offline> ++ <ask-for-message-on-offline type="bool">true</ask-for-message-on-offline> + <ask-for-message-on-online type="bool">false</ask-for-message-on-online> + <ask-for-message-on-chat type="bool">true</ask-for-message-on-chat> + <ask-for-message-on-away type="bool">true</ask-for-message-on-away> +@@ -650,7 +650,20 @@ QLineEdit#le_status_text { + <by-template type="bool">true</by-template> + <by-status type="bool">false</by-status> + </last-overwrite> +- <presets/> ++ <presets> ++ <m0> ++ <key type="QString">zone</key> ++ <force-priority type="bool">false</force-priority> ++ <status type="QString">dnd</status> ++ <message type="QString">In The Zone[TM]</message> ++ </m0> ++ <m1> ++ <key type="QString">sleep</key> ++ <force-priority type="bool">false</force-priority> ++ <status type="QString">offline</status> ++ <message type="QString">Sleeping the hell out of here.</message> ++ </m1> ++ </presets> + <presets-in-status-menus type="QString" comment="'yes', 'no' or 'submenu'">submenu</presets-in-status-menus> + <show-only-online-offline type="bool">false</show-only-online-offline> + <show-choose type="bool">true</show-choose> +@@ -690,5 +703,9 @@ QLineEdit#le_status_text { + </keychain> + </options> + <accounts comment="Account definitions and options"/> +- <plugins comment="Plugin options"/> ++ <plugins comment="Plugin options"> ++ <auto-load> ++ <omemo type="bool">true</omemo> ++ </auto-load> ++ </plugins> + </psi> +diff --git a/src/psi_profiles.cpp b/src/psi_profiles.cpp +index 77fbd169..4b4bca11 100644 +--- a/src/psi_profiles.cpp ++++ b/src/psi_profiles.cpp +@@ -79,8 +79,8 @@ void UserAccount::reset() + req_mutual_auth = false; + legacy_ssl_probe = false; + security_level = QCA::SL_None; +- ssl = SSL_Auto; +- jid = ""; ++ ssl = SSL_Yes; ++ jid = "@jid@"; + pass = ""; + scramSaltedHashPassword = ""; + opt_pass = false; +@@ -90,7 +90,7 @@ void UserAccount::reset() + opt_automatic_resource = true; + priority_dep_on_status = true; + ignore_global_actions = false; +- resource = ApplicationInfo::name(); ++ resource = "@resource@"; + priority = 55; + ibbOnly = false; + opt_keepAlive = true; +@@ -133,7 +133,7 @@ void UserAccount::reset() + << "stun.voipbuster.com" + << "stun.voxgratia.org"; + +- stunHost = stunHosts[0]; ++ stunHost = ""; + + keybind.clear(); + diff --git a/pkgs/aszlig/psi/darkstyle.patch b/pkgs/aszlig/psi/darkstyle.patch new file mode 100644 index 00000000..45c5067f --- /dev/null +++ b/pkgs/aszlig/psi/darkstyle.patch @@ -0,0 +1,32 @@ +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 7118ea75..c6f58e35 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -237,6 +237,7 @@ endif() + set(RESOURCES + ${PROJECT_SOURCE_DIR}/psi.qrc + ${PROJECT_SOURCE_DIR}/iconsets.qrc ++ ${QDARKSTYLE_PATH}/qdarkstyle/style.qrc + ) + qt5_add_resources(QRC_SOURCES ${RESOURCES}) + +diff --git a/src/main.cpp b/src/main.cpp +index b45fbab0..1cbead4a 100644 +--- a/src/main.cpp ++++ b/src/main.cpp +@@ -532,6 +532,15 @@ PSI_EXPORT_FUNC int main(int argc, char *argv[]) + QCoreApplication::addLibraryPath(appPath); + # endif + PsiApplication app(argc, argv); ++ ++ QFile darkstyle(":qdarkstyle/style.qss"); ++ if (!darkstyle.exists()) { ++ qWarning() << "Unable to set dark style"; ++ } else { ++ darkstyle.open(QFile::ReadOnly | QFile::Text); ++ QTextStream ts(&darkstyle); ++ app.setStyleSheet(ts.readAll()); ++ } + QApplication::setApplicationName(ApplicationInfo::name()); + QApplication::addLibraryPath(ApplicationInfo::resourcesDir()); + QApplication::addLibraryPath(ApplicationInfo::homeDir(ApplicationInfo::DataLocation)); diff --git a/pkgs/aszlig/psi/default.nix b/pkgs/aszlig/psi/default.nix new file mode 100644 index 00000000..d0e24c05 --- /dev/null +++ b/pkgs/aszlig/psi/default.nix @@ -0,0 +1,72 @@ +{ stdenv, lib, fetchFromGitHub, cmake, makeWrapper +, hunspell, libgcrypt, libgpgerror, libidn, libotr, libsForQt5 +, libsignal-protocol-c, libtidy, qt5 + +, substituteAll + +, jid ? "something@example.org" +, resource ? "psi-aszlig" +}: + +let + qdarkstyle = fetchFromGitHub { + owner = "ColinDuquesnoy"; + repo = "QDarkStyleSheet"; + rev = "c92d0c4c996e3e859134492e0f9f7f74bd0e12cd"; + sha256 = "1qrmp3ibvgzwh2v1qfrfh8xiwvj0kbhj1bm17bjx7zpmnb8byz3m"; + }; + +in stdenv.mkDerivation rec { + name = "psi-${version}"; + version = "2.0git20190922aszlig"; + + src = fetchFromGitHub { + owner = "psi-im"; + repo = "psi"; + rev = "af26ce1c04207d384a05ea530d571068310957c6"; + sha256 = "0pxd4ha391mfmsa7n7ag5kqw0nv825wsnkyfxi8wsa942bnbircg"; + fetchSubmodules = true; + }; + + plugins = fetchFromGitHub { + owner = "psi-im"; + repo = "plugins"; + rev = "5574afcc8ab4c5647831d38be111976fb1fa10d3"; + sha256 = "1h011j94iy40ksiqlprjamfyv7irql502hhhb0mpabk4mndxmjgn"; + }; + + patches = [ + ./disable-xep-0232.patch + ./darkstyle.patch + (substituteAll { + src = ./config.patch; + inherit jid resource; + }) + ]; + + preConfigure = '' + cp --no-preserve=all -rt src/plugins "$plugins"/* + ''; + + cmakeFlags = [ + "-DENABLE_PLUGINS=ON" "-DUSE_KEYCHAIN=OFF" "-DPSI_VERSION=${version}" + "-DQDARKSTYLE_PATH=${qdarkstyle}" + ]; + + enableParallelBuilding = true; + nativeBuildInputs = [ cmake makeWrapper qt5.wrapQtAppsHook ]; + buildInputs = [ + hunspell + libgcrypt + libgpgerror + libidn + libotr + libsForQt5.qca-qt5 + libsignal-protocol-c + libtidy + qt5.qtbase + qt5.qtmultimedia + qt5.qtwebengine + qt5.qtx11extras + ]; +} diff --git a/pkgs/aszlig/psi/disable-xep-0232.patch b/pkgs/aszlig/psi/disable-xep-0232.patch new file mode 100644 index 00000000..4788d262 --- /dev/null +++ b/pkgs/aszlig/psi/disable-xep-0232.patch @@ -0,0 +1,50 @@ +diff --git a/iris/src/xmpp/xmpp-im/client.cpp b/iris/src/xmpp/xmpp-im/client.cpp +index d8573bf..31af799 100644 +--- a/iris/src/xmpp/xmpp-im/client.cpp ++++ b/iris/src/xmpp/xmpp-im/client.cpp +@@ -1257,45 +1257,6 @@ DiscoItem Client::makeDiscoResult(const QString &node) const + + item.setFeatures(features); + +- // xep-0232 Software Information +- XData si; +- XData::FieldList si_fields; +- +- XData::Field si_type_field; +- si_type_field.setType(XData::Field::Field_Hidden); +- si_type_field.setVar("FORM_TYPE"); +- si_type_field.setValue(QStringList(QLatin1String("urn:xmpp:dataforms:softwareinfo"))); +- si_fields.append(si_type_field); +- +- XData::Field software_field; +- software_field.setType(XData::Field::Field_TextSingle); +- software_field.setVar("software"); +- software_field.setValue(QStringList(d->clientName)); +- si_fields.append(software_field); +- +- XData::Field software_v_field; +- software_v_field.setType(XData::Field::Field_TextSingle); +- software_v_field.setVar("software_version"); +- software_v_field.setValue(QStringList(d->clientVersion)); +- si_fields.append(software_v_field); +- +- XData::Field os_field; +- os_field.setType(XData::Field::Field_TextSingle); +- os_field.setVar("os"); +- os_field.setValue(QStringList(d->osName)); +- si_fields.append(os_field); +- +- XData::Field os_v_field; +- os_v_field.setType(XData::Field::Field_TextSingle); +- os_v_field.setVar("os_version"); +- os_v_field.setValue(QStringList(d->osVersion)); +- si_fields.append(os_v_field); +- +- si.setType(XData::Data_Result); +- si.setFields(si_fields); +- +- item.setExtensions(QList<XData>() << si); +- + return item; + } + diff --git a/pkgs/aszlig/santander/default.nix b/pkgs/aszlig/santander/default.nix deleted file mode 100644 index adac0a8d..00000000 --- a/pkgs/aszlig/santander/default.nix +++ /dev/null @@ -1,98 +0,0 @@ -{ stdenv, lib, fetchurl, fetchgit, fetchpatch, runCommand, p7zip, jq -, winePackages, pcsclite -}: - -let - patchedWine = let - libpcsclite = "${lib.getLib pcsclite}/lib/libpcsclite.so"; - in winePackages.minimal.overrideAttrs (drv: { - scard4wine = fetchgit { - url = "git://git.code.sf.net/p/scard4wine/code"; - rev = "c14c02c80bf1f2bb4cedd1f53a3a2ab9c48bed76"; - sha256 = "0ffmbl9mdnaih4h3ggpnzqbih3kgbwl3wv6j1ag5s4czn8gcpdq3"; - }; - - prePatch = (drv.prePatch or "") + '' - cp -t dlls/winscard "$scard4wine/src/"* - sed -i -re 's,"libpcsclite\.so(\.[0-9]+)*","${libpcsclite}",' \ - dlls/winscard/winscard.c - ''; - - patches = (drv.patches or []) ++ [ - ./winscard.patch - (fetchpatch { - url = "http://achurch.org/patch-pile/wine/3.0/disable-unixfs.diff"; - sha256 = "1yj3walwalya9g9aajcp4iygh348npp9dmks66r9dvwbd3fa8wcb"; - }) - ]; - - configureFlags = lib.toList (drv.configureFlags or []) ++ [ - "--disable-unixfs" - ]; - - postConfigure = (drv.postConfigure or "") + '' - # The wineprefix is within the Nix store, so let's ensure wine doesn't - # check the owner of the files: - sed -i -e '/HAVE_GETUID/d' include/config.h - ''; - }); - -in stdenv.mkDerivation rec { - name = "TRAVIC-Sign-${version}"; - version = "3.1.3.0"; - - src = fetchurl { - url = "https://service.santanderbank.de/special/banking/files/" - + "${name}-Installer.exe"; - sha256 = "19a14av3bg6i4iy5q5pa737cwxznqji0lcrapxw0q6qb8rs1rhs7"; - }; - - extensionId = "ilpoejcegjjlgpobjkpjmddkbdkdndaj"; - - buildInputs = [ p7zip jq ]; - - unpackCmd = "7z x -y -otavic-sign $curSrc"; - - phases = [ "unpackPhase" "patchPhase" "installPhase" ]; - - postPatch = '' - jq '.allowed_origins = [ - "chrome-extension://'"$extensionId"'/" - ] | .path = "'"$out/share/libexec/travic-sign/travic-sign"'" - | del(.allowed_extensions)' manifest-firefox.json > host.json - - 7z x -y -oextension FirefoxExtension.xpi - jq '.content_scripts[].matches = ["https://karte.santanderbank.de/*"] | { - # All the object attributes that we want to have (nothing more): - background, web_accessible_resources, content_scripts, page_action, - permissions, author, version, description, name, manifest_version - }' extension/manifest.json > new_manifest.json - mv new_manifest.json extension/manifest.json - (cd extension && 7z a -tzip ../travic-sign.crx *) - ''; - - winePrefix = runCommand "empty-wineprefix" { - buildInputs = [ patchedWine ]; - } '' - export WINEPREFIX="$out" - mkdir -p "$out" - wine wineboot.exe - ''; - - installPhase = '' - libexec="$out/share/libexec/travic-sign" - - install -vD -m 0644 TRAVIC-Sign-Service.exe "$libexec/service.exe" - install -vD -m 0644 host.json \ - "$out/etc/chromium/native-messaging-hosts/travic-sign.json" - install -vD -m 0644 travic-sign.crx \ - "$out/share/chromium/extensions/$extensionId.crx" - - cat > "$libexec/travic-sign" <<EOF - #!${stdenv.shell} - export WINEPREFIX="$winePrefix" - exec ${patchedWine}/bin/wine "$libexec/service.exe" - EOF - chmod +x "$libexec/travic-sign" - ''; -} diff --git a/pkgs/aszlig/santander/pipelight.patch b/pkgs/aszlig/santander/pipelight.patch deleted file mode 100644 index 3a07da72..00000000 --- a/pkgs/aszlig/santander/pipelight.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/windows/pluginloader/pluginloader.c b/src/windows/pluginloader/pluginloader.c -index 9e8556f..c50be2a 100644 ---- a/src/windows/pluginloader/pluginloader.c -+++ b/src/windows/pluginloader/pluginloader.c -@@ -1510,7 +1510,7 @@ void dispatcher(int functionid, Stack &stack){ - NPObject *objectValue; - NPError result; - -- if (variable == NPPVpluginScriptableNPObject) -+ if (variable == NPPVpluginScriptableNPObject && pluginFuncs.getvalue) - result = pluginFuncs.getvalue(instance, variable, &objectValue); - else{ - DBG_WARN("FUNCTION_NPP_GETVALUE_OBJECT - variable %d not allowed", variable); diff --git a/pkgs/aszlig/santander/winscard.patch b/pkgs/aszlig/santander/winscard.patch deleted file mode 100644 index 7dfa04ac..00000000 --- a/pkgs/aszlig/santander/winscard.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/dlls/winscard/winscard.c 1970-01-01 01:00:01.000000000 +0100 -+++ b/dlls/winscard/winscard.c 2016-06-06 01:52:53.631444433 +0200 -@@ -1527,7 +1527,7 @@ - { - LONG lRet; - TRACE(" 0x%08X %p %p %p %p %p %p\n",(unsigned int) hCard,mszReaderNames,pcchReaderLen,pdwState,pdwProtocol,pbAtr,pcbAtrLen); -- if(!pcchReaderLen || !pdwState || !pdwProtocol || !pcbAtrLen) -+ if(!pcchReaderLen || !pcbAtrLen) - lRet = SCARD_E_INVALID_PARAMETER; - else if(!liteSCardStatus) - lRet = SCARD_F_INTERNAL_ERROR; diff --git a/pkgs/aszlig/vim/default.nix b/pkgs/aszlig/vim/default.nix index 6c1c6bab..5a2a3962 100644 --- a/pkgs/aszlig/vim/default.nix +++ b/pkgs/aszlig/vim/default.nix @@ -335,6 +335,27 @@ let ''; installPhase = "cp -r data/syntax-highlighting/vim \"$out\""; }; + + jinja2 = stdenv.mkDerivation { + name = "jinja2-vim-${python3Packages.jinja2.version}"; + inherit (python3Packages.jinja2) src; + phases = [ "unpackPhase" "installPhase" ]; + installPhase = '' + install -vD -m 0644 ext/Vim/jinja.vim "$out/syntax/jinja.vim" + ''; + }; + + xdebug = fetchurl { + name = "vim-xt-syntax"; + url = "https://raw.githubusercontent.com/xdebug/xdebug/" + + "ce4f6bc7ae04ae542960af6c1b8975888e9c3e5e/contrib/xt.vim"; + sha256 = "05a3nry310s2w1h2q7w6yw2wick81jrnrs43x9vk0k7dqyavhvhi"; + downloadToTemp = true; + recursiveHash = true; + postFetch = '' + install -vD -m 0644 "$downloadedFile" "$out/syntax/xt.vim" + ''; + }; }; generic = '' @@ -384,6 +405,13 @@ let let g:ledger_commodity_before = 0 let g:ledger_commodity_sep = ' ' let g:ledger_fold_blanks = 1 + + " php + let php_noShortTags = 1 + let php_sql_query = 1 + let php_baselib = 1 + let php_htmlInStrings = 1 + let g:PHP_vintage_case_default_indent = 1 ''; autocmd = '' @@ -394,6 +422,7 @@ let " filetype defaults au BufNewFile,BufRead *.as setlocal ft=actionscript au BufNewFile,BufRead *.tt setlocal ft=tt2html ts=2 sw=2 sts=2 et + au BufNewFile,BufRead *.xt setlocal ft=xt foldlevel=4 au BufNewFile,BufRead *.html setlocal ts=2 sw=2 sts=2 et au FileType python setlocal textwidth=79 au FileType gitcommit setlocal textwidth=72 @@ -415,8 +444,8 @@ let " prevent colorscheme from overriding these highlights au ColorScheme * highlight ExtraWhitespace ctermbg=red guibg=red - " highlight everything exceeding 79 characters (except for CSV) - au BufWinEnter * if &ft !=# 'csv' + " highlight everything exceeding 79 characters (with exceptions) + au BufWinEnter * if index(['csv', 'strace', 'xt'], &ft) < 0 \ | let w:m2=matchadd('ErrorMsg', '\%>79v.\+', -1) \ | endif diff --git a/pkgs/build-support/build-sandbox/default.nix b/pkgs/build-support/build-sandbox/default.nix index 66797268..4e5cffe9 100644 --- a/pkgs/build-support/build-sandbox/default.nix +++ b/pkgs/build-support/build-sandbox/default.nix @@ -11,6 +11,10 @@ let pathsRuntimeVars = paths.runtimeVars or []; # Mount a dash shell in /bin/sh inside the chroot. allowBinSh = attrs.allowBinSh or false; + # Enable nix builds from within the sandbox. + # Has to write the full nix store to make the outputs accessible. + # TODO: get rid of nix & pkg-config if this is enabled (in the Makefile) + fullNixStore = attrs.fullNixStore or false; # Create code snippets for params.c to add extra_mount() calls. mkExtraMountParams = isRequired: lib.concatMapStringsSep "\n" (extra: let @@ -26,11 +30,13 @@ in stdenv.mkDerivation ({ inherit drv; + # writes files "sandbox-*" to the builder (see nix manual) exportReferencesGraph = [ "sandbox-closure" drv ] ++ lib.optionals allowBinSh [ "sandbox-binsh" dash ]; configurePhase = '' + # Reads the dependency closures and does … something? TODO: explain runtimeDeps="$(sed -ne ' p; n; n @@ -52,25 +58,36 @@ in stdenv.mkDerivation ({ echo '#include "setup.h"' > params.c echo 'bool setup_app_paths(void) {' >> params.c - for dep in $runtimeDeps; do - echo 'if (!bind_mount("'"$dep"'", true, true, true)) return false;' \ + ${if fullNixStore then '' + # /nix/var needs to be writable for nix to work inside the sandbox + echo 'if (!bind_mount("/nix/var", false, true, true)) return false;' \ + >> params.c + echo 'if (!bind_mount("/nix/store", true, true, true)) return false;' \ >> params.c - done + + '' else '' + for dep in $runtimeDeps; do + echo 'if (!bind_mount("'"$dep"'", true, true, true)) return false;' \ + >> params.c + done + ''} ${mkExtraMountParams true pathsRequired} ${mkExtraMountParams false pathsWanted} echo 'return true; }' >> params.c - echo 'bool mount_runtime_path_vars(struct query_state *qs) {' >> params.c + ${lib.optionalString (!fullNixStore) '' + echo 'bool mount_runtime_path_vars(struct query_state *qs) {' >> params.c - ${lib.concatMapStringsSep "\n" (pathvar: let - escaped = lib.escapeShellArg (lib.escape ["\\" "\""] pathvar); - fun = "mount_from_path_var"; - result = "echo 'if (!${fun}(qs, \"'${escaped}'\")) return false;'"; - in "${result} >> params.c") pathsRuntimeVars} + ${lib.concatMapStringsSep "\n" (pathvar: let + escaped = lib.escapeShellArg (lib.escape ["\\" "\""] pathvar); + fun = "mount_from_path_var"; + result = "echo 'if (!${fun}(qs, \"'${escaped}'\")) return false;'"; + in "${result} >> params.c") pathsRuntimeVars} - echo 'return true; }' >> params.c + echo 'return true; }' >> params.c + ''} ''; postInstall = '' @@ -82,8 +99,9 @@ in stdenv.mkDerivation ({ ''; nativeBuildInputs = [ pkgconfig ]; - buildInputs = [ nix boost ]; + buildInputs = [ boost nix ]; makeFlags = [ "BINDIR=${drv}/bin" ] - ++ lib.optional allowBinSh "BINSH_EXECUTABLE=${dash}/bin/dash"; + ++ lib.optional allowBinSh "BINSH_EXECUTABLE=${dash}/bin/dash" + ++ lib.optional fullNixStore "FULL_NIX_STORE=1"; } // removeAttrs attrs [ "paths" "allowBinSh" ]) diff --git a/pkgs/build-support/build-sandbox/src/Makefile b/pkgs/build-support/build-sandbox/src/Makefile index e18ec9d4..8e1218f6 100644 --- a/pkgs/build-support/build-sandbox/src/Makefile +++ b/pkgs/build-support/build-sandbox/src/Makefile @@ -1,19 +1,24 @@ BINARIES = $(wildcard $(BINDIR)/*) WRAPPERS = $(subst $(BINDIR),$(out)/bin,$(BINARIES)) +OBJECTS = path-cache.o params.o setup.o +CFLAGS = -g -Wall -std=gnu11 -DFS_ROOT_DIR=\"$(out)\" +CXXFLAGS = -g -Wall -std=c++14 `pkg-config --cflags nix-main` +LDFLAGS = -Wl,--copy-dt-needed-entries `pkg-config --libs nix-main` + +ifdef FULL_NIX_STORE +CFLAGS += -DFULL_NIX_STORE +else +OBJECTS += nix-query.o NIX_VERSION = `pkg-config --modversion nix-main | \ sed -e 's/^\([0-9]\+\)\.\([0-9][0-9]\).*/\1\2/' \ -e 's/^\([0-9]\+\)\.\([0-9]\).*/\10\2/'` +CXXFLAGS += -DNIX_VERSION=$(NIX_VERSION) +endif -OBJECTS = nix-query.o path-cache.o params.o setup.o - -CFLAGS = -g -Wall -std=gnu11 -DFS_ROOT_DIR=\"$(out)\" ifdef BINSH_EXECUTABLE CFLAGS += -DBINSH_EXECUTABLE=\"$(BINSH_EXECUTABLE)\" endif -CXXFLAGS = -g -Wall -std=c++14 `pkg-config --cflags nix-main` -CXXFLAGS += -DNIX_VERSION=$(NIX_VERSION) -LDFLAGS = -Wl,--copy-dt-needed-entries `pkg-config --libs nix-main` all: $(OBJECTS) diff --git a/pkgs/build-support/build-sandbox/src/setup.c b/pkgs/build-support/build-sandbox/src/setup.c index feafd6f6..98205710 100644 --- a/pkgs/build-support/build-sandbox/src/setup.c +++ b/pkgs/build-support/build-sandbox/src/setup.c @@ -9,7 +9,6 @@ #include <fcntl.h> #include <libgen.h> #include <limits.h> -#include <malloc.h> #include <sched.h> #include <stdbool.h> #include <stdio.h> @@ -18,8 +17,10 @@ #include <unistd.h> #include "params.h" -#include "nix-query.h" #include "path-cache.h" +#ifndef FULL_NIX_STORE +#include "nix-query.h" +#endif static path_cache cached_paths = NULL; @@ -604,6 +605,7 @@ static bool setup_binsh(const char *executable) } #endif +#ifndef FULL_NIX_STORE static bool is_dir(const char *path) { struct stat sb; @@ -663,6 +665,9 @@ bool mount_from_path_var(struct query_state *qs, const char *name) return true; } +/* `/etc/static` is a special symlink on NixOS, pointing to a storepath + of configs that have to be available at runtime for some programs + to function. So we need to mount the closure of that storepath. */ static bool setup_static_etc(struct query_state *qs) { char dest[PATH_MAX]; @@ -680,6 +685,7 @@ static bool setup_static_etc(struct query_state *qs) return mount_requisites(qs, dest); } +/* Bind-mount all necessary nix store paths. */ static bool setup_runtime_paths(void) { struct query_state *qs; @@ -702,6 +708,7 @@ static bool setup_runtime_paths(void) free_query(qs); return true; } +#endif static bool setup_runtime_debug(void) { @@ -782,8 +789,11 @@ static bool setup_chroot(void) if (!bind_mount("/tmp", false, true, false)) return false; + // We don’t need to query the nix store if we mount the full store +#ifndef FULL_NIX_STORE if (!setup_runtime_paths()) return false; +#endif if (!setup_app_paths()) return false; @@ -864,9 +874,25 @@ bool setup_sandbox(void) /* Just wait in the parent until the child exits. We need to fork because * otherwise we can't mount /proc in the right PID namespace. */ + int wstatus; if (pid > 0) { - waitpid(pid, NULL, 0); - _exit(1); + + if (waitpid(pid, &wstatus, 0) == -1) { + fputs("sandbox: waitpid failure", stderr); + _exit(EXIT_FAILURE); + } + else if (WIFEXITED(wstatus)) { + _exit(WEXITSTATUS(wstatus)); + } + else if (WIFSIGNALED(wstatus)) { + fprintf(stderr, "sandbox: killed by signal %d\n", WTERMSIG(wstatus)); + _exit(EXIT_FAILURE); + } + else { + // WIFSTOPPED, WIFCONTINUED? + fputs("sandbox: wait failed", stderr); + _exit(EXIT_FAILURE); + } } cached_paths = new_path_cache(); diff --git a/pkgs/games/gog/crosscode.nix b/pkgs/games/gog/crosscode.nix index 9e60ab08..f33cdb45 100644 --- a/pkgs/games/gog/crosscode.nix +++ b/pkgs/games/gog/crosscode.nix @@ -2,12 +2,12 @@ buildGame rec { name = "crosscode-${version}"; - version = "1.0.2"; + version = "1.1.0"; src = fetchGog { productId = 1252295864; downloadName = "en3installer0"; - sha256 = "0gd3i99g79w7nr6dnkjkpfq5s2y20dwrf706ipzkggknygmg9xad"; + sha256 = "1rqf1vlg151hxy5f9nwldmb4l3853dmvcf7fiakab8vzsmjmldlm"; }; nativeBuildInputs = [ makeWrapper ]; diff --git a/pkgs/games/gog/default.nix b/pkgs/games/gog/default.nix index 1ef58c30..f1010a00 100644 --- a/pkgs/games/gog/default.nix +++ b/pkgs/games/gog/default.nix @@ -15,13 +15,17 @@ let crosscode = callPackage ./crosscode.nix {}; dungeons3 = callPackage ./dungeons3.nix {}; epistory = callPackage ./epistory.nix { }; + homm3 = callPackage ./homm3 {}; + kingdoms-and-castles = callPackage ./kingdoms-and-castles.nix {}; overload = callPackage ./overload.nix {}; party-hard = callPackage ./party-hard.nix {}; satellite-reign = callPackage ./satellite-reign.nix {}; settlers2 = callPackage ./settlers2.nix {}; stardew-valley = callPackage ./stardew-valley.nix {}; + the-longest-journey = callPackage ./the-longest-journey {}; thimbleweed-park = callPackage ./thimbleweed-park.nix {}; war-for-the-overworld = callPackage ./war-for-the-overworld.nix {}; + warcraft2 = callPackage ./warcraft2 {}; wizard-of-legend = callPackage ./wizard-of-legend.nix {}; xeen = callPackage ./xeen.nix {}; }; diff --git a/pkgs/games/gog/fetch-gog/default.nix b/pkgs/games/gog/fetch-gog/default.nix index b49f0dab..c1d350ce 100644 --- a/pkgs/games/gog/fetch-gog/default.nix +++ b/pkgs/games/gog/fetch-gog/default.nix @@ -147,13 +147,24 @@ let } ''; - in runCommandCC "get-captcha" { - nativeBuildInputs = [ pkgconfig ]; + in stdenv.mkDerivation { + name = "get-captcha"; + + dontUnpack = true; + + nativeBuildInputs = [ pkgconfig (qt5.wrapQtAppsHook or null) ]; buildInputs = [ qt5.qtbase qt5.qtwebengine ]; - } '' - g++ $(pkg-config --libs --cflags Qt5WebEngineWidgets Qt5WebEngine) \ - -Wall -std=c++11 -o "$out" ${application} - ''; + preferLocalBuild = true; + + buildPhase = '' + g++ $(pkg-config --libs --cflags Qt5WebEngineWidgets Qt5WebEngine) \ + -Wall -std=c++11 -o get-captcha ${application} + ''; + + installPhase = '' + install -vD get-captcha "$out/bin/get-captcha" + ''; + }; mkPyStr = str: "'${stdenv.lib.escape ["'" "\\"] (toString str)}'"; @@ -177,7 +188,7 @@ let def login(self): browser = mechanicalsoup.StatefulBrowser() response = browser.open(${mkPyStr authURL}) - if "google.com/recaptcha" in response.text: + if "https://www.recaptcha.net/recaptcha" in response.text: token_url = self.login_with_captcha() else: browser.select_form('form[name="login"]') @@ -185,13 +196,19 @@ let browser['login[password]'] = ${mkPyStr password} browser.submit_selected() - auth_code = parse_qs(urlsplit(browser.get_url()).query)['code'] + query = parse_qs(urlsplit(browser.get_url()).query) + + if 'code' not in query: + sys.stderr.write( + "Unable to login with the provided GOG credentials.\n" + ) + raise SystemExit(1) token_url = "https://auth.gog.com/token?" + urlencode({ 'client_id': ${mkPyStr clientId}, 'client_secret': ${mkPyStr clientSecret}, 'grant_type': 'authorization_code', - 'code': auth_code, + 'code': query['code'], 'redirect_uri': ${mkPyStr redirectUri} }) @@ -203,7 +220,8 @@ let def login_with_captcha(self): sys.stderr.write("Solving a captcha is required to log in.\n") - sys.stderr.write("Please run " ${mkPyStr getCaptcha} " now.\n") + sys.stderr.write("Please run " ${mkPyStr getCaptcha} + "/bin/get-captcha now.\n") sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sys.stderr.write("Waiting for connection") i = 0 @@ -278,6 +296,8 @@ in stdenv.mkDerivation { outputHashAlgo = "sha256"; outputHash = sha256; + preferLocalBuild = true; + nativeBuildInputs = [ curl python3Packages.tabulate python3Packages.MechanicalSoup ]; diff --git a/pkgs/games/gog/homm3/default.nix b/pkgs/games/gog/homm3/default.nix new file mode 100644 index 00000000..5aecb8f2 --- /dev/null +++ b/pkgs/games/gog/homm3/default.nix @@ -0,0 +1,97 @@ +{ stdenv, lib, buildSandbox, fetchGog, runCommand, makeWrapper, fetchFromGitHub +, cmake, pkgconfig, python3, boost, zlib, minizip, qt5 +, SDL2, SDL2_image, SDL2_mixer, SDL2_ttf +, innoextract, parallel, ffmpeg +}: + +let + data = runCommand "homm3-complete-data" rec { + version = "4.0"; + + # We need a newer version that 1.7, because GOG uses a newer archive + # format. + nativeBuildInputs = lib.singleton (innoextract.overrideAttrs (drv: { + src = fetchFromGitHub { + owner = "dscharrer"; + repo = "innoextract"; + rev = "4c61bc4da822fc89f2e05bdb2c45e6c4dd7a3673"; + sha256 = "197pr7dzlza4isssvhqhvnrr7wzc9c4b3wnnp03sxpmhviyidln1"; + }; + })) ++ [ parallel ffmpeg ]; + + data = fetchGog { + name = "setup_homm_3_complete_${version}.bin"; + productId = 1207658787; + downloadName = "en1installer1"; + sha256 = "1wfly3024yi64kaczfdca4wx5g09053dpc1gwp08w637833n4kq4"; + }; + + setup = fetchGog { + name = "setup_homm_3_complete_${version}.exe"; + productId = 1207658787; + downloadName = "en1installer0"; + sha256 = "1cwr28ml9z3iq6q9z1vs1jkbnjjrkv2m39bhqw78a5hvj43mgxza"; + }; + } '' + ln -s "$data" archive-1.bin + ln -s "$setup" archive.exe + innoextract -L -I Data -I Maps -I Mp3 archive.exe + mkdir -p "$out/music" + parallel -v ffmpeg -hide_banner -loglevel warning -i {} -acodec libvorbis \ + "$out/music/{/.}.ogg" ::: mp3/*.mp3 + mv -t "$out" data maps + ''; + + engine = stdenv.mkDerivation rec { + name = "vcmi-${version}"; + version = "20190609"; + + src = fetchFromGitHub { + owner = "vcmi"; + repo = "vcmi"; + rev = "e7bced112cf36007da8f418ba3313d2dd4b3e045"; + sha256 = "0qk0mpz3amg2kw5m99bk3qi19rwcwjj6s1lclby1ws0v8nxh2cmb"; + fetchSubmodules = true; + }; + + inherit data; + + patches = [ ./launcher-execl.patch ]; + + postPatch = '' + find -type f -name '*.cpp' -exec sed -i -e '/^ *# *include/ { + s!["<]SDL_\(ttf\|image\|mixer\)\.h[">]!<SDL2/SDL_\1.h>! + }' {} + + + sed -i -e 's/"Mp3"/"music"/' config/filesystem.json + ''; + + cmakeFlags = [ "-DCMAKE_INSTALL_LIBDIR=lib" "-DENABLE_TEST=0" ]; + enableParallelBuilding = true; + nativeBuildInputs = [ cmake pkgconfig python3 makeWrapper ]; + buildInputs = [ + boost zlib minizip SDL2 SDL2_image SDL2_mixer SDL2_ttf ffmpeg + qt5.qtbase + ]; + postInstall = let + inherit (qt5.qtbase) qtPluginPrefix; + qtPlugins = "${qt5.qtbase}/${qtPluginPrefix}"; + in '' + rm "$out/bin/vcmibuilder" + for i in "$out/bin/"*; do + rpath="$(patchelf --print-rpath "$i")" + patchelf --set-rpath "$out/lib/vcmi:$rpath" "$i" + done + + wrapProgram "$out/bin/vcmilauncher" \ + --suffix QT_PLUGIN_PATH : ${lib.escapeShellArg qtPlugins} + cp -rst "$out/share/vcmi" "$data"/* + ''; + dontStrip = true; + }; + +in buildSandbox engine { + allowBinSh = true; + paths.required = [ "$XDG_DATA_HOME/vcmi" "$XDG_CONFIG_HOME/vcmi" ]; + paths.runtimeVars = [ "LD_LIBRARY_PATH" "LOCALE_ARCHIVE" ]; +} diff --git a/pkgs/games/gog/homm3/launcher-execl.patch b/pkgs/games/gog/homm3/launcher-execl.patch new file mode 100644 index 00000000..fae0fa80 --- /dev/null +++ b/pkgs/games/gog/homm3/launcher-execl.patch @@ -0,0 +1,36 @@ +diff --git a/launcher/mainwindow_moc.cpp b/launcher/mainwindow_moc.cpp +index a07774ed2..3275af71a 100644 +--- a/launcher/mainwindow_moc.cpp ++++ b/launcher/mainwindow_moc.cpp +@@ -11,7 +11,7 @@ + #include "mainwindow_moc.h" + #include "ui_mainwindow_moc.h" + +-#include <QProcess> ++#include <unistd.h> + #include <QDir> + + #include "../lib/CConfigHandler.h" +@@ -77,19 +77,11 @@ void MainWindow::on_startGameButton_clicked() + + void MainWindow::startExecutable(QString name) + { +- QProcess process; +- +- // Start the executable +- if(process.startDetached(name)) +- { +- close(); // exit launcher +- } +- else +- { ++ if (execl(name.toLatin1().data(), "vcmiclient", nullptr) == -1) { ++ QString msg("Failed to start %1\nReason: %2"); + QMessageBox::critical(this, + "Error starting executable", +- "Failed to start " + name + "\n" +- "Reason: " + process.errorString(), ++ msg.arg(name).arg(strerror(errno)), + QMessageBox::Ok, + QMessageBox::Ok); + return; diff --git a/pkgs/games/gog/kingdoms-and-castles.nix b/pkgs/games/gog/kingdoms-and-castles.nix new file mode 100644 index 00000000..e31551cc --- /dev/null +++ b/pkgs/games/gog/kingdoms-and-castles.nix @@ -0,0 +1,14 @@ +{ buildUnity, fetchGog }: + +buildUnity { + name = "kingdoms-and-castles"; + fullName = "KingdomsAndCastles"; + saveDir = "LionShield/Kingdoms and Castles"; + version = "115r12"; + + src = fetchGog { + productId = 2067763543; + downloadName = "en3installer0"; + sha256 = "1ag03piq09z7hljcbs145hyj8z0gjcvffj99znf3mnbw2qipb7pq"; + }; +} diff --git a/pkgs/games/gog/the-longest-journey/default.nix b/pkgs/games/gog/the-longest-journey/default.nix new file mode 100644 index 00000000..9dca199a --- /dev/null +++ b/pkgs/games/gog/the-longest-journey/default.nix @@ -0,0 +1,106 @@ +{ stdenv, lib, fetchGog, fetchFromGitHub, innoextract, runCommand, buildSandbox +, SDL2, SDL2_net, freetype, libGLU_combined, glew, alsaLib +, libogg, libvorbis, xvfb_run +}: + +let + gameData = runCommand "the-longest-journey-data" rec { + version = "142"; + + # We need a newer version that 1.7, because GOG uses a newer archive + # format. + nativeBuildInputs = lib.singleton (innoextract.overrideAttrs (drv: { + src = fetchFromGitHub { + owner = "dscharrer"; + repo = "innoextract"; + rev = "4c61bc4da822fc89f2e05bdb2c45e6c4dd7a3673"; + sha256 = "197pr7dzlza4isssvhqhvnrr7wzc9c4b3wnnp03sxpmhviyidln1"; + }; + })); + + data = fetchGog { + name = "the-longest-journey-${version}.bin"; + productId = 1207658794; + downloadName = "en1installer1"; + sha256 = "08jg5snlxkzxppq37lsmbhgv9zhwnk1zr4cid5gynzq9b1048rzc"; + }; + + setup = fetchGog { + name = "the-longest-journey-${version}.exe"; + productId = 1207658794; + downloadName = "en1installer0"; + sha256 = "1h4c2bhf5mhz004r37dwdydl3rhpg1wyr4kyvxxwma7x9grxqyzc"; + }; + } '' + ln -s "$data" archive-1.bin + ln -s "$setup" archive.exe + innoextract -L -m archive.exe + mkdir "$out" + mv -t "$out" \ + game.exe gui.ini chapters.ini language.ini x.xarc \ + static global fonts [a-f0-9][a-f0-9] + ''; + + residualvm = stdenv.mkDerivation rec { + name = "residualvm-${version}"; + version = "20190611"; + + src = fetchFromGitHub { + owner = "residualvm"; + repo = "residualvm"; + rev = "ae1a7fbf6fa6bf88a7adebaedb2cd713d5ccc718"; + sha256 = "1521578jis9s3ilz0ws0msanviyqf70dp54db3d6ssfikc0w3myx"; + }; + + patches = [ ./predefined-config.patch ]; + + # Current Git version has an --enable-static option so the stdenv setup + # thinks that there is --disable-static as well, which doesn't exist. + dontDisableStatic = true; + + enableParallelBuilding = true; + buildInputs = [ + SDL2 SDL2_net freetype libGLU_combined glew alsaLib + libogg libvorbis + ]; + + configureFlags = [ "--disable-all-engines" "--enable-engine=stark" ]; + }; + + configFile = runCommand "residualvm-stark.ini" { + nativeBuildInputs = [ xvfb_run residualvm ]; + inherit gameData; + } '' + xvfb-run residualvm -p "$gameData" -a + sed -e '/^\[residualvm\]/a enable_unsupported_game_warning=false' \ + residualvm.ini > "$out" + ''; + + unsandboxed = runCommand "the-longest-journey-${gameData.version}" { + residualCmd = "${residualvm}/bin/residualvm"; + configArgs = let + mkXdg = what: fallback: extra: let + basePath = "\${XDG_${what}_HOME:-$HOME/${fallback}}"; + in "\"${basePath}/the-longest-journey${extra}\""; + in [ + "--savepath=${mkXdg "DATA" ".local/share" ""}" + "--config=${mkXdg "CONFIG" ".config" "/settings.ini"}" + ]; + inherit (stdenv) shell; + residualArgs = lib.escapeShellArgs [ "--predefined-config=${configFile}" ]; + } '' + mkdir -p "$out/bin" + cat > "$out/bin/the-longest-journey" <<EOF + #!$shell + exec $residualCmd $residualArgs $configArgs "\$@" tlj-win + EOF + chmod +x "$out/bin/the-longest-journey" + ''; + +in buildSandbox unsandboxed { + paths.required = [ + "$XDG_CONFIG_HOME/the-longest-journey" + "$XDG_DATA_HOME/the-longest-journey" + ]; + paths.runtimeVars = [ "LD_LIBRARY_PATH" ]; +} diff --git a/pkgs/games/gog/the-longest-journey/predefined-config.patch b/pkgs/games/gog/the-longest-journey/predefined-config.patch new file mode 100644 index 00000000..504e3867 --- /dev/null +++ b/pkgs/games/gog/the-longest-journey/predefined-config.patch @@ -0,0 +1,29 @@ +diff --git a/base/commandLine.cpp b/base/commandLine.cpp +index ab741917..8723fc0d 100644 +--- a/base/commandLine.cpp ++++ b/base/commandLine.cpp +@@ -425,6 +425,9 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha + DO_LONG_COMMAND("list-saves") + END_COMMAND + ++ DO_LONG_OPTION("predefined-config") ++ END_OPTION ++ + DO_OPTION('c', "config") + END_OPTION + +diff --git a/base/main.cpp b/base/main.cpp +index 2fbfc679..d74b15e5 100644 +--- a/base/main.cpp ++++ b/base/main.cpp +@@ -394,6 +394,10 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { + Common::StringMap settings; + command = Base::parseCommandLine(settings, argc, argv); + ++ // Load config file with predefined options ++ if (settings.contains("predefined-config")) ++ ConfMan.loadConfigFile(settings["predefined-config"]); ++ + // Load the config file (possibly overridden via command line): + if (settings.contains("config")) { + ConfMan.loadConfigFile(settings["config"]); diff --git a/pkgs/games/gog/warcraft2/default.nix b/pkgs/games/gog/warcraft2/default.nix new file mode 100644 index 00000000..c576439f --- /dev/null +++ b/pkgs/games/gog/warcraft2/default.nix @@ -0,0 +1,148 @@ +{ stdenv, lib, buildSandbox, writeTextFile, runCommand, fetchGog, fetchurl +, fetchFromGitHub, winePackages, xvfb_run, ffmpeg, rename + +# Dependencies for the Stratagus engine +, cmake, pkgconfig, toluapp, lua5_1, libpng, libmng, zlib, SDL, fluidsynth +, bzip2, libmikmod, libogg, libvorbis, libtheora, libGLU_combined, sqlite +}: + +let + timgm6mb = fetchurl { + name = "TimGM6mb.sf2"; + url = "https://sourceforge.net/p/mscore/code/3412/" + + "tree/trunk/mscore/share/sound/TimGM6mb.sf2?format=raw"; + sha256 = "0m68a5z43nirirq9rj2xzz6z5qpyhdwk40s83sqhr4lc09i8ndy5"; + }; + + stratagus = stdenv.mkDerivation { + name = "stratagus-${version}"; + version = "2.4.2git20190615"; + + src = fetchFromGitHub { + owner = "Wargus"; + repo = "stratagus"; + rev = "c7fc80ff7e89ab969d867121b6f679f81ea60ecb"; + sha256 = "1n39lxd8qg03kw884llcal3h95y34lys44hib2mdb3qhd5dg9a18"; + }; + + patches = [ ./xdg.patch ]; + + # Both check_version and detectPresence in addition to a bunch of functions + # in stratagus-tinyfiledialogs.h are trying to run various tools via + # /bin/sh, so let's NOP them out. + postPatch = '' + sed -i -e '/^int/!s/\(check_version\|detectPresence\)([^)]*)/1/g' \ + gameheaders/stratagus-game-launcher.h + sed -i -e '/^\(static \+\)\?int/!s/[a-zA-Z0-9]\+Present *([^)]*)/0/g' \ + gameheaders/stratagus-tinyfiledialogs.h + ''; + + NIX_CFLAGS_COMPILE = [ "-Wno-error=format-overflow" ]; + cmakeFlags = [ + "-DOpenGL_GL_PREFERENCE=GLVND" "-DENABLE_DEV=ON" + "-DGAMEDIR=${placeholder "out"}/bin" + ]; + nativeBuildInputs = [ cmake pkgconfig toluapp ]; + buildInputs = [ + toluapp lua5_1 libpng libmng zlib SDL fluidsynth bzip2 libmikmod libogg + libvorbis libtheora libGLU_combined sqlite + ]; + }; + + stormlib = stdenv.mkDerivation { + name = "stormlib-${version}"; + version = "9.22git20190615"; + + src = fetchFromGitHub { + owner = "ladislav-zezula"; + repo = "StormLib"; + rev = "2f0e0e69e6b3739d7c450ac3d38816aee45ac3c2"; + sha256 = "04f43c6bwfxiiw1kplxb3ds8g9r633y587z8ir97hrrzw5nmni3w"; + }; + + nativeBuildInputs = [ cmake ]; + buildInputs = [ zlib bzip2 ]; + }; + + wargus = stdenv.mkDerivation { + name = "wargus-${version}"; + version = "2.4.2git20190615"; + + src = fetchFromGitHub { + owner = "Wargus"; + repo = "wargus"; + rev = "932c4974dfea9805f6710f254de191e65dadb50d"; + sha256 = "13vpfd4yx43n5sqzj79km7gcv9al3mqskkij335f0c9p28rqf47v"; + }; + + # This fixes up the data path by letting it be set using an environment + # variable later in the wrapper and also hardcodes the MuseScore sound + # font. + # + # In addition, wartool.cpp contains a few lines like this: + # + # sprintf(extract, "%s.something", extract); + # + # The problem here is that sprintf() modifies the data pointed by extract + # *IN PLACE*, so this essentially truncates the contents to ".something". + # + # While it may be even better to use strncat() here, the application isn't + # critical for security, so for the sake of laziness, let's just use + # strcat() instead. + postPatch = '' + sed -i -e ' + s/^\( *# *define \+DATA_PATH\).*/\1 getenv("WARGUS_DATAPATH")/ + ' wargus.cpp + + sed -i -e 's!"[^"]*\/TimGM6mb.sf2"!"${timgm6mb}"!g' \ + wargus.cpp scripts/stratagus.lua + + sed -i -e ' + s/sprintf( *\([^,]\+\), "%s\([^"]\+\)", *\1 *)/strcat(\1, "\2")/ + ' wartool.cpp + + # XXX: Crashes the game, see https://github.com/Wargus/wargus/issues/260 + rm -r scripts/lists/campaigns/For\ the\ Motherland\ * + ''; + + cmakeFlags = [ "-DGAMEDIR=${placeholder "out"}/bin" ]; + nativeBuildInputs = [ cmake pkgconfig ]; + buildInputs = [ stratagus zlib libpng bzip2 stormlib ]; + }; + + version = "2.02.4"; + + # XXX: Unfortunately, innoextract (even Git master) doesn't support this + # archive, so let's resort to a headless wine for extraction. + gameData = runCommand "warcraft2-data-${version}" { + src = fetchGog { + name = "setup-warcraft-2-${version}.exe"; + productId = 1418669891; + downloadName = "en1installer0"; + sha256 = "1cf3c1ylgdrvkk7y25v47f66m6lp9m4wvl2aldpxzrrqdrlk34k3"; + }; + nativeBuildInputs = [ winePackages.minimal xvfb_run ffmpeg wargus rename ]; + } '' + export WINEPREFIX="$PWD" + wine wineboot.exe + xvfb-run -s '-screen 0 1024x768x24' wine "$src" /sp- /lang=english /silent + gameDir='drive_c/GOG Games/Warcraft II BNE' + find "$gameDir" -mindepth 1 -depth \ + -exec rename 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \; + wartool -v -r "$gameDir" "$out" + cp -rnst "$out" ${lib.escapeShellArg wargus}/share/games/stratagus/wargus/* + ''; + +in buildSandbox (writeTextFile { + name = "warcraft2-${version}"; + destination = "/bin/warcraft2"; + executable = true; + text = '' + #!${stdenv.shell} + export WARGUS_DATAPATH=${lib.escapeShellArg gameData} + exec ${lib.escapeShellArg "${wargus}/bin/wargus"} "$@" + ''; +}) { + paths.required = [ "$XDG_DATA_HOME/wc2" ]; + paths.runtimeVars = [ "LD_LIBRARY_PATH" ]; +} diff --git a/pkgs/games/gog/warcraft2/xdg.patch b/pkgs/games/gog/warcraft2/xdg.patch new file mode 100644 index 00000000..979a86e3 --- /dev/null +++ b/pkgs/games/gog/warcraft2/xdg.patch @@ -0,0 +1,37 @@ +diff --git a/src/stratagus/parameters.cpp b/src/stratagus/parameters.cpp +index a705ba21d..8585856b0 100644 +--- a/src/stratagus/parameters.cpp ++++ b/src/stratagus/parameters.cpp +@@ -48,26 +48,13 @@ void Parameters::SetDefaultValues() + + void Parameters::SetDefaultUserDirectory() + { +-#ifdef USE_GAME_DIR +- userDirectory = StratagusLibPath; +-#elif USE_WIN32 +- userDirectory = getenv("APPDATA"); +-#else +- userDirectory = getenv("HOME"); +-#endif +- +- if (!userDirectory.empty()) { +- userDirectory += "/"; ++ const char *xdg_data_home = getenv("XDG_DATA_HOME"); ++ if (xdg_data_home == NULL) { ++ userDirectory = getenv("HOME"); ++ userDirectory += "/.local/share"; ++ } else { ++ userDirectory = xdg_data_home; + } +- +-#ifdef USE_GAME_DIR +-#elif USE_WIN32 +- userDirectory += "Stratagus"; +-#elif defined(USE_MAC) +- userDirectory += "Library/Stratagus"; +-#else +- userDirectory += ".stratagus"; +-#endif + } + + static std::string GetLocalPlayerNameFromEnv() diff --git a/pkgs/games/humblebundle/default.nix b/pkgs/games/humblebundle/default.nix index 2193ca50..5de3f40e 100644 --- a/pkgs/games/humblebundle/default.nix +++ b/pkgs/games/humblebundle/default.nix @@ -25,6 +25,7 @@ let jamestown = callPackage ./jamestown.nix {}; liads = callPackage ./liads.nix {}; megabytepunch = callPackage_i686 ./megabytepunch.nix {}; + minimetro = callPackage ./minimetro.nix {}; opus-magnum = callPackage ./opus-magnum.nix {}; owlboy = callPackage ./owlboy.nix {}; pico-8 = callPackage ./pico-8.nix {}; diff --git a/pkgs/games/humblebundle/fetch-humble-bundle/default.nix b/pkgs/games/humblebundle/fetch-humble-bundle/default.nix index 063d4a02..1340bce8 100644 --- a/pkgs/games/humblebundle/fetch-humble-bundle/default.nix +++ b/pkgs/games/humblebundle/fetch-humble-bundle/default.nix @@ -1,13 +1,20 @@ -{ stdenv, curl, cacert, writeText, fetchFromGitHub, fetchpatch -, python, pythonPackages +{ stdenv, curl, cacert, writeText, writeScript, fetchFromGitHub, fetchpatch +, python, python3, pythonPackages # Dependencies for the captcha solver -, pkgconfig, qt5, runCommandCC +, pkgconfig, qt5 , email, password }: -{ name ? null, machineName, downloadName ? "Download", suffix ? "humblebundle", md5 }: let +{ name ? null +, machineName +, downloadName ? "Download" +, suffix ? "humblebundle" +, md5 +}: + +let cafile = "${cacert}/etc/ssl/certs/ca-bundle.crt"; getCaptcha = let @@ -77,12 +84,34 @@ } ''; - in runCommandCC "get-captcha" { - nativeBuildInputs = [ pkgconfig ]; + in stdenv.mkDerivation { + name = "get-captcha"; + + dontUnpack = true; + + nativeBuildInputs = [ pkgconfig (qt5.wrapQtAppsHook or null) ]; buildInputs = [ qt5.qtbase qt5.qtwebengine ]; - } '' - g++ $(pkg-config --libs --cflags Qt5WebEngineWidgets Qt5WebEngine) \ - -Wall -std=c++11 -o "$out" ${application} + preferLocalBuild = true; + + buildPhase = '' + g++ $(pkg-config --libs --cflags Qt5WebEngineWidgets Qt5WebEngine) \ + -Wall -std=c++11 -o get-captcha ${application} + ''; + + installPhase = '' + install -vD get-captcha "$out/bin/get-captcha" + ''; + }; + + getGuard = writeScript "get-guard" '' + #!${python3.interpreter} + import socket + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.bind(('localhost', 18129)) + sock.listen(1) + with sock.accept()[0] as conn: + guard = input("Guard code: ") + conn.sendall(guard.encode()) ''; humbleAPI = pythonPackages.buildPythonPackage rec { @@ -96,6 +125,8 @@ sha256 = "1kcg42nh7sbjabim1pbqx14468pypznjy7fx2bv7dicy0sqd9b8j"; }; + patches = [ ./guard-code.patch ]; + postPatch = '' sed -i -e '/^LOGIN_URL *=/s,/login,/processlogin,' humblebundle/client.py sed -i -e '/self\.supports_canonical.*data.*supports_canonical/d' \ @@ -137,7 +168,8 @@ def login_with_captcha(hb): print >>sys.stderr, "Solving a captcha is required to log in." - print >>sys.stderr, "Please run " ${pyStr (toString getCaptcha)} " now." + print >>sys.stderr, "Please run " ${pyStr (toString getCaptcha)} \ + "/bin/get-captcha now." sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print >>sys.stderr, "Waiting for connection", i = 0 @@ -152,13 +184,41 @@ response = sock.recv(4096) sock.close() print >>sys.stderr, "Captcha solved correctly, logging in." - hb.login(${pyStr email}, ${pyStr password}, recaptcha_response=response) + api_login(hb, recaptcha_response=response) + + def login_with_guard(hb, skip_code): + print >>sys.stderr, "A guard code has been sent to your email address." + print >>sys.stderr, "Please run " ${pyStr (toString getGuard)} " now." + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + print >>sys.stderr, "Waiting for connection", + # XXX: DRY! + i = 0 + while sock.connect_ex(("127.0.0.1", 18129)) != 0: + time.sleep(0.1) + if i % 10 == 0: + sys.stderr.write('.') + sys.stderr.flush() + i += 1 + print >>sys.stderr, " connected." + print >>sys.stderr, "Waiting for guard code..." + response = sock.recv(4096) + sock.close() + print >>sys.stderr, "Guard code supplied, logging in." + api_login(hb, captcha_skip_code=skip_code, guard_code=response) + + def api_login(hb, recaptcha_response=None, + captcha_skip_code=None, guard_code=None): + try: + hb.login(${pyStr email}, ${pyStr password}, + recaptcha_response=recaptcha_response, + captcha_skip_code=captcha_skip_code, guard_code=guard_code) + except humblebundle.exceptions.HumbleCaptchaException: + login_with_captcha(hb) + except humblebundle.exceptions.HumbleGuardRequiredException as e: + login_with_guard(hb, e.captcha_skip_code) hb = humblebundle.HumbleApi() - try: - hb.login(${pyStr email}, ${pyStr password}) - except humblebundle.exceptions.HumbleCaptchaException: - login_with_captcha(hb) + api_login(hb) products = dict(get_products(hb)) dstruct = find_download(products) @@ -183,6 +243,7 @@ in stdenv.mkDerivation { outputHashAlgo = "md5"; outputHash = md5; + preferLocalBuild = true; buildInputs = [ python humbleAPI ]; buildCommand = '' diff --git a/pkgs/games/humblebundle/fetch-humble-bundle/guard-code.patch b/pkgs/games/humblebundle/fetch-humble-bundle/guard-code.patch new file mode 100644 index 00000000..f17928ae --- /dev/null +++ b/pkgs/games/humblebundle/fetch-humble-bundle/guard-code.patch @@ -0,0 +1,121 @@ +diff --git a/humblebundle/client.py b/humblebundle/client.py +index fbc31c9..44184a1 100644 +--- a/humblebundle/client.py ++++ b/humblebundle/client.py +@@ -75,7 +75,9 @@ class HumbleApi(object): + """ + + @callback +- def login(self, username, password, authy_token=None, recaptcha_challenge=None, recaptcha_response=None, ++ def login(self, username, password, authy_token=None, ++ recaptcha_challenge=None, recaptcha_response=None, ++ guard_code=None, captcha_skip_code=None, + *args, **kwargs): + """ + Login to the Humble Bundle API. The response sets the _simpleauth_sess cookie which is stored in the session +@@ -87,6 +89,8 @@ class HumbleApi(object): + :type authy_token: integer or str + :param str recaptcha_challenge: (optional) The challenge signed by Humble Bundle's public key from reCAPTCHA + :param str recaptcha_response: (optional) The plaintext solved CAPTCHA ++ :param str guard_code: (optional) The guard code sent via email ++ :param str captcha_skip_code: (optional) A token to skip the CAPTCHA + :param list args: (optional) Extra positional args to pass to the request + :param dict kwargs: (optional) Extra keyword args to pass to the request. If a data dict is supplied a key + collision with any of the above params will resolved in favor of the supplied param +@@ -108,7 +112,9 @@ class HumbleApi(object): + 'password': password, + 'authy-token': authy_token, + 'recaptcha_challenge_field': recaptcha_challenge, +- 'recaptcha_response_field': recaptcha_response} ++ 'recaptcha_response_field': recaptcha_response, ++ 'guard': guard_code, ++ 'captcha-skip-code': captcha_skip_code} + kwargs.setdefault('data', {}).update({k: v for k, v in default_data.items() if v is not None}) + + response = self._request('POST', LOGIN_URL, *args, **kwargs) +diff --git a/humblebundle/exceptions.py b/humblebundle/exceptions.py +index 9041219..fe4eeaf 100644 +--- a/humblebundle/exceptions.py ++++ b/humblebundle/exceptions.py +@@ -9,7 +9,7 @@ __copyright__ = "Copyright 2014, Joel Pedraza" + __license__ = "MIT" + + __all__ = ['HumbleException', 'HumbleResponseException', 'HumbleAuthenticationException', 'HumbleCredentialException', +- 'HumbleCaptchaException', 'HumbleTwoFactorException', 'HumbleParseException'] ++ 'HumbleCaptchaException', 'HumbleTwoFactorException', 'HumbleGuardRequiredException', 'HumbleParseException'] + + from requests import RequestException + +@@ -38,6 +38,7 @@ class HumbleAuthenticationException(HumbleResponseException): + def __init__(self, *args, **kwargs): + self.captcha_required = kwargs.pop('captcha_required', None) + self.authy_required = kwargs.pop('authy_required', None) ++ self.captcha_skip_code = kwargs.pop('captcha_skip_code', None) + super(HumbleAuthenticationException, self).__init__(*args, **kwargs) + + +@@ -62,6 +63,13 @@ class HumbleTwoFactorException(HumbleAuthenticationException): + pass + + ++class HumbleGuardRequiredException(HumbleAuthenticationException): ++ """ ++ A guard code is required ++ """ ++ pass ++ ++ + class HumbleParseException(HumbleResponseException): + """ + An error occurred while parsing +diff --git a/humblebundle/handlers.py b/humblebundle/handlers.py +index 36fc6e1..a8acebf 100644 +--- a/humblebundle/handlers.py ++++ b/humblebundle/handlers.py +@@ -64,29 +64,42 @@ def login_handler(client, response): + success = data.get('success', None) + if success is True: + return True ++ if data.get('goto', None) is not None: ++ return True + + captcha_required = data.get('captcha_required') + authy_required = data.get('authy_required') ++ captcha_skip_code = data.get('skip_code', [None])[0] ++ ++ guard = data.get('humble_guard_required', False) ++ if guard: ++ raise HumbleGuardRequiredException('Guard code required', request=response.request, response=response, ++ captcha_required=captcha_required, authy_required=authy_required, ++ captcha_skip_code=captcha_skip_code) + + errors, error_msg = get_errors(data) + if errors: + captcha = errors.get('captcha') + if captcha: + raise HumbleCaptchaException(error_msg, request=response.request, response=response, +- captcha_required=captcha_required, authy_required=authy_required) ++ captcha_required=captcha_required, authy_required=authy_required, ++ captcha_skip_code=captcha_skip_code) + + username = errors.get('username') + if username: + raise HumbleCredentialException(error_msg, request=response.request, response=response, +- captcha_required=captcha_required, authy_required=authy_required) ++ captcha_required=captcha_required, authy_required=authy_required, ++ captcha_skip_code=captcha_skip_code) + + authy_token = errors.get("authy-token") + if authy_token: + raise HumbleTwoFactorException(error_msg, request=response.request, response=response, +- captcha_required=captcha_required, authy_required=authy_required) ++ captcha_required=captcha_required, authy_required=authy_required, ++ captcha_skip_code=captcha_skip_code) + + raise HumbleAuthenticationException(error_msg, request=response.request, response=response, +- captcha_required=captcha_required, authy_required=authy_required) ++ captcha_required=captcha_required, authy_required=authy_required, ++ captcha_skip_code=captcha_skip_code) + + + def gamekeys_handler(client, response): diff --git a/pkgs/games/humblebundle/minimetro.nix b/pkgs/games/humblebundle/minimetro.nix new file mode 100644 index 00000000..a5c48775 --- /dev/null +++ b/pkgs/games/humblebundle/minimetro.nix @@ -0,0 +1,15 @@ +{ buildUnity, fetchHumbleBundle }: + +buildUnity rec { + name = "minimetro"; + version = "39"; + fullName = "Mini Metro"; + saveDir = "Dinosaur Polo Club/Mini Metro"; + + src = fetchHumbleBundle { + name = "MiniMetro-release-39-linux.tar.gz"; + machineName = "minimetro_linux"; + downloadName = "Download"; + md5 = "3e7afefbcc68b6295821394e31f5e48b"; + }; +} diff --git a/pkgs/games/itch/fetch-itch/default.nix b/pkgs/games/itch/fetch-itch/default.nix index 3700f5c6..121868be 100644 --- a/pkgs/games/itch/fetch-itch/default.nix +++ b/pkgs/games/itch/fetch-itch/default.nix @@ -69,6 +69,7 @@ in stdenv.mkDerivation { SSL_CERT_FILE = "${cacert}/etc/ssl/certs/ca-bundle.crt"; + preferLocalBuild = true; nativeBuildInputs = [ python3Packages.python ]; buildCommand = '' diff --git a/pkgs/games/steam/fetchsteam/default.nix b/pkgs/games/steam/fetchsteam/default.nix index 5c1faf55..646e0a14 100644 --- a/pkgs/games/steam/fetchsteam/default.nix +++ b/pkgs/games/steam/fetchsteam/default.nix @@ -79,6 +79,7 @@ let in with stdenv.lib; runCommand "${name}-src" { buildInputs = [ DepotDownloader ]; inherit username password appId depotId manifestId; + preferLocalBuild = true; outputHashAlgo = "sha256"; outputHash = sha256; outputHashMode = "recursive"; diff --git a/pkgs/profpatsch/default.nix b/pkgs/profpatsch/default.nix index a4378ecf..dc746c2e 100644 --- a/pkgs/profpatsch/default.nix +++ b/pkgs/profpatsch/default.nix @@ -5,11 +5,25 @@ let # wrapper for execlineb that doesn’t need the execline commands # in PATH to work (making them appear like “builtins”) + # TODO: upstream into nixpkgs + # TODO: the grep could be nicer execlineb-with-builtins = let eldir = "${pkgs.execline}/bin"; in pkgs.writeScriptBin "execlineb" '' #!${eldir}/execlineb -s0 + # appends the execlineb bin dir to PATH if not yet in PATH ${eldir}/define eldir ${eldir} + ''${eldir}/ifelse + { + # since this is nix, we can grep for the execline drv hash in PATH + # to see whether it’s already in there + ''${eldir}/pipeline + { ${pkgs.coreutils}/bin/printenv PATH } + ${pkgs.gnugrep}/bin/grep --quiet "${eldir}" + } + # it’s there already + { ''${eldir}/execlineb $@ } + # not there yet, add it ''${eldir}/importas oldpath PATH ''${eldir}/export PATH "''${eldir}:''${oldpath}" ''${eldir}/execlineb $@ @@ -46,6 +60,52 @@ let allowSubstitutes = false; }) cmd; + testing = import ./testing { + inherit stdenv lib; + inherit (runExeclineFns) runExecline; + inherit (pkgs) runCommand; + bin = bins pkgs.s6PortableUtils [ "s6-touch" "s6-echo" ]; + }; + + runExeclineFns = + # todo: factor out calling tests + let + it = import ./execline/run-execline.nix { + bin = (bins execlineb-with-builtins [ "execlineb" ]) + // (bins pkgs.execline [ "redirfd" "importas" "exec" ]); + inherit stdenv lib; + }; + itLocal = name: args: execline: + it name (args // { + derivationArgs = args.derivationArgs or {} // { + preferLocalBuild = true; + allowSubstitutes = false; + }; + }) execline; + + tests = import ./execline/run-execline-tests.nix { + # can’t use runExeclineLocal in the tests, + # because it is tested by the tests (well, it does + # work, but then you have to run the tests every time) + runExecline = it; + inherit (testing) drvSeqL; + inherit (pkgs) coreutils; + inherit stdenv; + bin = (bins execlineb-with-builtins [ "execlineb" ]) + // (bins pkgs.execline [ + { use = "if"; as = "execlineIf"; } + "redirfd" "importas" + ]) + // (bins pkgs.s6PortableUtils + [ "s6-cat" "s6-grep" "s6-touch" "s6-test" "s6-chmod" ]); + }; + in { + runExecline = it; + runExeclineLocal = name: args: execline: + testing.drvSeqL tests (itLocal name args execline); + }; + + in rec { inherit (nixperiments) # filterSource by parsing a .gitignore file @@ -60,10 +120,11 @@ in rec { json2json json2string; backlight = callPackage ./backlight { inherit (pkgs.xorg) xbacklight; }; - display-infos = callPackage ./display-infos {}; + display-infos = callPackage ./display-infos { inherit sfttime; }; git-commit-index = callPackage ./git-commit-index { inherit script runCommandLocal; }; nix-http-serve = callPackage ./nix-http-serve {}; nman = callPackage ./nman {}; + sfttime = callPackage ./sfttime {}; show-qr-code = callPackage ./show-qr-code {}; warpspeed = callPackage ./warpspeed { inherit (pkgs.haskellPackages) ghcWithPackages; @@ -90,35 +151,8 @@ in rec { ]; }); - runExecline = - # todo: factor out calling tests - let - it = import ./execline/run-execline.nix { - bin = (bins execlineb-with-builtins [ "execlineb" ]) - // (bins pkgs.execline [ "redirfd" "importas" "exec" ]); - inherit stdenv; - }; - tests = import ./execline/run-execline-tests.nix { - runExecline = it; - inherit (testing) drvSeqL; - inherit (pkgs) coreutils; - inherit stdenv; - bin = (bins execlineb-with-builtins [ "execlineb" ]) - // (bins pkgs.execline [ - { use = "if"; as = "execlineIf"; } - "redirfd" "importas" - ]) - // (bins pkgs.s6PortableUtils - [ "s6-cat" "s6-grep" "s6-touch" "s6-test" "s6-chmod" ]); - }; - in tests; - - - testing = import ./testing { - inherit stdenv lib runExecline; - inherit (pkgs) runCommand; - bin = bins pkgs.s6PortableUtils [ "s6-touch" "s6-echo" ]; - }; + inherit (runExeclineFns) + runExecline runExeclineLocal; symlink = pkgs.callPackage ./execline/symlink.nix { inherit runExecline; diff --git a/pkgs/profpatsch/display-infos/default.nix b/pkgs/profpatsch/display-infos/default.nix index d213241c..b626e844 100644 --- a/pkgs/profpatsch/display-infos/default.nix +++ b/pkgs/profpatsch/display-infos/default.nix @@ -1,8 +1,8 @@ -{ lib, runCommand, python3, libnotify }: +{ lib, runCommand, writeText, python3, libnotify, bc, sfttime }: let name = "display-infos-0.1.0"; - script = builtins.toFile (name + "-script") '' + script = writeText (name + "-script") '' #!@python3@ import sys @@ -11,20 +11,53 @@ let import os.path as path import statistics as st + def readint(fn): + with open(fn, 'r') as f: + return int(f.read()) + + def seconds_to_sft(secs): + p = sub.Popen(["@bc@", "-l"], stdin=sub.PIPE, stdout=sub.PIPE) + (sft, _) = p.communicate(input="scale=2; obase=16; {} / 86400\n".format(secs).encode()) + p.terminate() + return str(sft.strip().decode()) + + charging = readint("/sys/class/power_supply/AC/online") + full = 0 now = 0 + # this is "to charged" if charging and "to empty" if not + seconds_remaining = 0 for bat in glob.iglob("/sys/class/power_supply/BAT*"): - def readint(fn): - with open(fn, 'r') as f: - return int(f.read()) + # these files might be different for different ACPI/battery providers + # see the full list in acpi.c of the acpi(1) tool + # unit: who knows full += readint(path.join(bat, "energy_full")) now += readint(path.join(bat, "energy_now" )) + # in unit?/hours, hopefully the same unit as above + # ACPI is a garbage fire + current_rate = readint(path.join(bat, "power_now")) + + if current_rate == 0: + continue + elif charging: + seconds_remaining += 3600 * (full - now) / current_rate + else: + seconds_remaining += 3600 * now / current_rate bat = round( now/full, 2 ) + ac = "🗲 " if charging else "" + sft_remaining = seconds_to_sft(seconds_remaining) date = sub.run(["date", "+%d.%m. %a %T"], stdout=sub.PIPE).stdout.strip().decode() - notify = "BAT: {}% | {}".format(int(bat*100), date) - sub.run(["@notify-send@", notify]) + sftdate = sub.run(["@sfttime@"], stdout=sub.PIPE).stdout.strip().decode() + notify = "BAT: {percent}% {ac}{charge}| {date} | {sftdate}".format( + percent = int(bat*100), + ac = ac, + charge = "{} ".format(sft_remaining) if seconds_remaining else "", + date = date, + sftdate = sftdate + ) + print(notify) ''; in @@ -33,6 +66,7 @@ in } '' substitute ${script} script \ --replace "@python3@" "${getBin python3}/bin/python3" \ - --replace "@notify-send@" "${getBin libnotify}/bin/notify-send" + --replace "@bc@" "${getBin bc}/bin/bc" \ + --replace "@sfttime@" "${getBin sfttime}/bin/sfttime" install -D script $out/bin/display-infos '' diff --git a/pkgs/profpatsch/execline/escape.nix b/pkgs/profpatsch/execline/escape.nix new file mode 100644 index 00000000..d9a0be0c --- /dev/null +++ b/pkgs/profpatsch/execline/escape.nix @@ -0,0 +1,30 @@ +{ lib }: +let + # replaces " and \ to \" and \\ respectively and quote with " + # e.g. + # a"b\c -> "a\"b\\c" + # a\"bc -> "a\\\"bc" + # TODO upsteam into nixpkgs + escapeExeclineArg = arg: + ''"${builtins.replaceStrings [ ''"'' ''\'' ] [ ''\"'' ''\\'' ] (toString arg)}"''; + + # Escapes an execline (list of execline strings) to be passed to execlineb + # Give it a nested list of strings. Nested lists are interpolated as execline + # blocks ({}). + # Everything is quoted correctly. + # + # Example: + # escapeExecline [ "if" [ "somecommand" ] "true" ] + # == ''"if" { "somecommand" } "true"'' + escapeExecline = execlineList: lib.concatStringsSep " " + (let + go = arg: + if builtins.isString arg then [(escapeExeclineArg arg)] + else if lib.isDerivation arg then [(escapeExeclineArg arg)] + else if builtins.isList arg then [ "{" ] ++ builtins.concatMap go arg ++ [ "}" ] + else abort "escapeExecline can only hande nested lists of strings, was ${lib.generators.toPretty {} arg}"; + in builtins.concatMap go execlineList); + +in { + inherit escapeExecline; +} diff --git a/pkgs/profpatsch/execline/run-execline-tests.nix b/pkgs/profpatsch/execline/run-execline-tests.nix index ebfdeb20..c3f534cc 100644 --- a/pkgs/profpatsch/execline/run-execline-tests.nix +++ b/pkgs/profpatsch/execline/run-execline-tests.nix @@ -2,26 +2,24 @@ # https://www.mail-archive.com/skaware@list.skarnet.org/msg01256.html , coreutils }: -# TODO: run all of these locally! runExeclineLocal - let # lol - writeScript = name: script: runExecline { - inherit name; + writeScript = name: script: runExecline name { derivationArgs = { inherit script; passAsFile = [ "script" ]; + preferLocalBuild = true; + allowSubstitutes = false; }; - execline = '' - importas -ui s scriptPath - importas -ui out out - foreground { - ${coreutils}/bin/mv $s $out - } - ${bin.s6-chmod} 0755 $out - ''; - }; + } [ + "importas" "-ui" "s" "scriptPath" + "importas" "-ui" "out" "out" + "foreground" [ + "${coreutils}/bin/mv" "$s" "$out" + ] + "${bin.s6-chmod}" "0755" "$out" + ]; # execline block of depth 1 block = args: builtins.map (arg: " ${arg}") args ++ [ "" ]; @@ -30,7 +28,7 @@ let # in the given file. Does not use runExecline, because # that should be tested after all. fileHasLine = line: file: derivation { - name = "file-${file.name}-has-line"; + name = "run-execline-test-file-${file.name}-has-line"; inherit (stdenv) system; builder = bin.execlineIf; args = @@ -43,41 +41,49 @@ let bin.importas "-ui" "out" "out" bin.s6-touch "$out" ]; + preferLocalBuild = true; + allowSubstitutes = false; }; # basic test that touches out - basic = runExecline { - name = "basic"; - execline = '' - importas -ui out out - ${bin.s6-touch} $out - ''; - }; + basic = runExecline "run-execline-test-basic" { + derivationArgs = { + preferLocalBuild = true; + allowSubstitutes = false; + }; + } [ + "importas" "-ui" "out" "out" + "${bin.s6-touch}" "$out" + ]; # whether the stdin argument works as intended - stdin = fileHasLine "foo" (runExecline { - name = "stdin"; + stdin = fileHasLine "foo" (runExecline "run-execline-test-stdin" { stdin = "foo\nbar\nfoo"; - execline = '' - importas -ui out out + derivationArgs = { + preferLocalBuild = true; + allowSubstitutes = false; + }; + } [ + "importas" "-ui" "out" "out" # this pipes stdout of s6-cat to $out # and s6-cat redirects from stdin to stdout - redirfd -w 1 $out ${bin.s6-cat} - ''; - }); + "redirfd" "-w" "1" "$out" bin.s6-cat + ]); - wrapWithVar = runExecline { - name = "wrap-with-var"; + wrapWithVar = runExecline "run-execline-test-wrap-with-var" { builderWrapper = writeScript "var-wrapper" '' #!${bin.execlineb} -S0 export myvar myvalue $@ ''; - execline = '' - importas -ui v myvar - if { ${bin.s6-test} myvalue = $v } - importas out out - ${bin.s6-touch} $out - ''; - }; + derivationArgs = { + preferLocalBuild = true; + allowSubstitutes = false; + }; + } [ + "importas" "-ui" "v" "myvar" + "if" [ bin.s6-test "myvalue" "=" "$v" ] + "importas" "out" "out" + bin.s6-touch "$out" + ]; -in args: drvSeqL [ basic stdin wrapWithVar ] (runExecline args) +in [ basic stdin wrapWithVar ] diff --git a/pkgs/profpatsch/execline/run-execline.nix b/pkgs/profpatsch/execline/run-execline.nix index dbc6f4fd..2efe43d6 100644 --- a/pkgs/profpatsch/execline/run-execline.nix +++ b/pkgs/profpatsch/execline/run-execline.nix @@ -1,15 +1,18 @@ -{ stdenv, bin }: -{ name -# the execline script as string -, execline +{ stdenv, bin, lib }: +name: +{ # a string to pass as stdin to the execline script -, stdin ? "" +stdin ? "" # a program wrapping the acutal execline invocation; # should be in Bernstein-chaining style , builderWrapper ? bin.exec # additional arguments to pass to the derivation , derivationArgs ? {} }: +# the execline script as a nested list of string, +# representing the blocks; +# see docs of `escapeExecline`. + execline: # those arguments can’t be overwritten assert !derivationArgs ? system; @@ -18,6 +21,7 @@ assert !derivationArgs ? builder; assert !derivationArgs ? args; derivation (derivationArgs // { + # TODO: what about cross? inherit (stdenv) system; inherit name; @@ -26,7 +30,10 @@ derivation (derivationArgs // { # to pass the script and stdin as envvar; # this might clash with another passed envar, # so we give it a long & unique name - _runExeclineScript = execline; + _runExeclineScript = + let + escape = (import ./escape.nix { inherit lib; }); + in escape.escapeExecline execline; _runExeclineStdin = stdin; passAsFile = [ "_runExeclineScript" diff --git a/pkgs/profpatsch/execline/symlink.nix b/pkgs/profpatsch/execline/symlink.nix index ca8684d2..c6a311d8 100644 --- a/pkgs/profpatsch/execline/symlink.nix +++ b/pkgs/profpatsch/execline/symlink.nix @@ -11,9 +11,7 @@ let "${toString (builtins.stringLength s)}:${s},"; in -runExecline { - inherit name; - +runExecline name { derivationArgs = { pathTuples = lib.concatMapStrings ({dest, orig}: toNetstring @@ -23,28 +21,26 @@ runExecline { # bah! coreutils just for cat :( PATH = lib.makeBinPath [ s6-portable-utils ]; }; +} [ + "importas" "-ui" "p" "pathTuplesPath" + "importas" "-ui" "out" "out" + "forbacktickx" "-d" "" "destorig" [ "${coreutils}/bin/cat" "$p" ] + "importas" "-ui" "do" "destorig" + "multidefine" "-d" "" "$do" [ "destsuffix" "orig" ] + "define" "dest" ''''${out}/''${destsuffix}'' - execline = '' - importas -ui p pathTuplesPath - importas -ui out out - forbacktickx -d "" destorig { ${coreutils}/bin/cat $p } - importas -ui do destorig - multidefine -d "" $do { destsuffix orig } - define dest ''${out}/''${destsuffix} - - # this call happens for every file, not very efficient - foreground { - backtick -n d { s6-dirname $dest } - importas -ui d d - s6-mkdir -p $d - } + # this call happens for every file, not very efficient + "foreground" [ + "backtick" "-n" "d" [ "s6-dirname" "$dest" ] + "importas" "-ui" "d" "d" + "s6-mkdir" "-p" "$d" + ] - ifthenelse { s6-test -L $orig } { - backtick -n res { s6-linkname -f $orig } - importas -ui res res - s6-ln -fs $res $dest - } { - s6-ln -fs $orig $dest - } - ''; -} + "ifthenelse" [ "s6-test" "-L" "$orig" ] [ + "backtick" "-n" "res" [ "s6-linkname" "-f" "$orig" ] + "importas" "-ui" "res" "res" + "s6-ln" "-fs" "$res" "$dest" + ] [ + "s6-ln" "-fs" "$orig" "$dest" + ] +] diff --git a/pkgs/profpatsch/nman/default.nix b/pkgs/profpatsch/nman/default.nix index b9b967cf..bbf4f00f 100644 --- a/pkgs/profpatsch/nman/default.nix +++ b/pkgs/profpatsch/nman/default.nix @@ -6,7 +6,9 @@ runCommandNoCC "nman" { license = licenses.gpl3; }; } '' - ${lib.getBin go}/bin/go build -o nman ${./nman.go} + mkdir cache + env GOCACHE="$PWD/cache" \ + ${lib.getBin go}/bin/go build -o nman ${./nman.go} install -D nman $out/bin/nman '' diff --git a/pkgs/profpatsch/sfttime/default.nix b/pkgs/profpatsch/sfttime/default.nix new file mode 100644 index 00000000..29d9170b --- /dev/null +++ b/pkgs/profpatsch/sfttime/default.nix @@ -0,0 +1,14 @@ +{ stdenv, makeWrapper, bc }: + +stdenv.mkDerivation { + name = "sfttime"; + + phases = [ "installPhase" "fixupPhase" ]; + buildInputs = [ makeWrapper ]; + + installPhase = '' + install -D ${./sfttime.sh} $out/bin/sfttime + wrapProgram $out/bin/sfttime \ + --prefix PATH : ${stdenv.lib.makeBinPath [ bc ]} + ''; +} diff --git a/pkgs/profpatsch/sfttime/sfttime.sh b/pkgs/profpatsch/sfttime/sfttime.sh new file mode 100755 index 00000000..949341b7 --- /dev/null +++ b/pkgs/profpatsch/sfttime/sfttime.sh @@ -0,0 +1,145 @@ +#!/usr/bin/env bash +# this script was created in 3BBE.81[sft]. +# usage: +# to convert to sfttime: +# $0 [c date] [digitcount] [nodate] +# if date is given, converts the given date to sfttime. +# if date is not given, converts the current date to sfttime. +# digitcount specifies the accuracy for the time part. +# nodate hides the date part. +# to convert from sfttime: +# $0 r sfttime [unix] +# converts the given sfttime to 'standard' time. +# if 'unix' is provided, the output will be in unix time. +# to show info about sfttime units +# $0 i [[sft]]$num +# displays name of unit [sft]$num, as well as it's value +# in both days and 'standard' units. + +SFT_EPOCH_UNIX=49020 + +case $1 in + "c") + unixtime=$(date --date="$2" +%s.%N) + shift + shift + mode=fw + ;; + "r") + shift + sfttime=$1 + if [[ $sfttime =~ ^([0-9A-F]*(.[0-9A-F]+)?)(\[[sS][fF][tT]\])?$ ]] && [[ $sfttime ]]; then + sfttime=${BASH_REMATCH[1]} + else + echo "error" 2>&1 + exit 1 + fi + shift + mode=bw + ;; + "i") + shift + inforeq=$1 + if [[ $inforeq =~ ^(\[[sS][fF][tT]\])?(-?[0-9]+)$ ]]; then + inforeq=${BASH_REMATCH[2]} + let inforeq=$inforeq + mode=in + elif [[ $inforeq =~ ^(\[[sS][fF][tT]\])?[eE][pP][oO][cC][hH]$ ]]; then + echo "[sft]epoch:" + echo "unix time $SFT_EPOCH_UNIX" + echo "1970-01-01 13:37:00 UTC" + exit 0 + else + echo "error" 2>&1 + exit 1 + fi + shift + mode=in + ;; + *) + unixtime=$(date +%s.%N) + mode=fw + ;; +esac + +case $mode in + "fw") + sfttime=$(echo "obase=16; ($unixtime-$SFT_EPOCH_UNIX)/86400" | bc -l) + if [[ $1 -ge 1 ]]; then + digits=$1 + shift + elif [[ ! $1 ]] || [[ $1 == nodate ]]; then + digits=3 + else + digits=0 + fi + + if [[ $sfttime =~ ^([0-9A-F]+)[.]([0-9A-F]{$digits}).*$ ]]; then + date=${BASH_REMATCH[1]} + time=${BASH_REMATCH[2]} + else + echo "Error" &1>2 + exit 1 + fi + + if [[ $digits -eq 0 ]]; then + echo "$date[sft]" + else + if [[ $1 == nodate ]]; then + echo ".$time[sft]" + shift + else + echo "$date.$time[sft]" + fi + fi + ;; + "bw") + unixtime=$(echo "ibase=16; $sfttime*15180+BF7C" | bc -l) + case $1 in + unix) + shift + echo $unixtime + ;; + *) + date --date="1970-01-01 $unixtime sec" + ;; + esac + ;; + "in") + name="[sft]$inforeq" + case $inforeq in + -4) newname="[sft]tick";; + -3) newname="[sft]tentacle";; + -2) newname="[sft]schinken";; + -1) newname="[sft]major";; + 0) newname="day";; + 1) newname="[sft]vergil";; + 2) newname="[sft]stallman";; + 3) newname="[sft]odin";; + esac + if [[ $newname ]]; then + echo "alternative name for $name: $newname" + name="$newname" + fi + one="1 $name" + echo "$one after [sft]epoch:" + sfttime=$(echo "obase=16; 16^$inforeq" | bc -l)[sft] + echo $sfttime + echo "time equivalent of $one:" + echo "the duration of $(echo 794243384928000*16^$inforeq | bc -l) periods of the radiation corresponding to the transition between the two hyperfine levels of the ground state of the caesium 133 atom" + + echo "standard time units equivalent:" + seconds=$(echo "86400*16^$inforeq" | bc -l) + if [[ $(echo "$seconds < 60" | bc -l) == 1 ]]; then + echo "$seconds seconds" + elif [[ $(echo "$seconds < 3600" | bc -l) == 1 ]]; then + echo "$(echo $seconds/60 | bc -l) minutes" + elif [[ $(echo "$seconds < 86400" | bc -l) == 1 ]]; then + echo "$(echo $seconds/3600 | bc -l) hours" + elif [[ $(echo "$seconds < 86400*365.2425" | bc -l) == 1 ]]; then + echo "$(echo $seconds/86400 | bc -l) days" + else + echo "$(echo $seconds/86400/365.2425 | bc -l) years" + fi + ;; +esac diff --git a/pkgs/profpatsch/utils-hs/default.nix b/pkgs/profpatsch/utils-hs/default.nix index 423feb03..9ddd50ec 100644 --- a/pkgs/profpatsch/utils-hs/default.nix +++ b/pkgs/profpatsch/utils-hs/default.nix @@ -53,11 +53,23 @@ let rev = "e7efbb4f0624e86109acd818942c8cd18a7d9d3d"; sha256 = "0dismb9vl5fxynasc2kv5baqyzp6gpyybmd5p9g1hlcq3p7pfi24"; }; + broken = false; buildDepends = old.buildDepends or [] ++ (with hself; [ dependent-sum prettyprinter (hlib.doJailbreak ref-tf) ]); }); - } );# // (import /home/philip/kot/dhall/overlay.nix { inherit haskell fetchFromGitHub; } hself hsuper)); + + dhall-nix = hlib.justStaticExecutables (hlib.overrideCabal hsuper.dhall-nix (old: { + src = fetchFromGitHub { + owner = "Profpatsch"; + repo = "dhall-nix"; + # manual update to dhall @0.19 + rev = "feae0ce5b2ecf4daeeae15c39f427f126c33da7c"; + sha256 = "1kdsbnj681lf65dsdclcrzj4cab1hh0v22n2140386zvwmawyp6r"; + }; + broken = false; + })); + }); }; haskellDrv = { name, subfolder, deps }: hps.mkDerivation { diff --git a/pkgs/profpatsch/xmonad/DhallTypedInput.hs b/pkgs/profpatsch/xmonad/DhallTypedInput.hs new file mode 100644 index 00000000..18c32b22 --- /dev/null +++ b/pkgs/profpatsch/xmonad/DhallTypedInput.hs @@ -0,0 +1,232 @@ +{-# language RecordWildCards, NamedFieldPuns, OverloadedStrings, ScopedTypeVariables, KindSignatures, DataKinds, ScopedTypeVariables, RankNTypes, GADTs, TypeApplications, AllowAmbiguousTypes, LambdaCase #-} +{- Exports the `inputWithTypeArgs` function, which is able to read dhall files of the normalized form + +@ +\(CustomType: Type) -> +\(AnotherType: Type) -> +… +@ + +and set their actual representation on the Haskell side: + +This has various advantages: + +- dhall files still type check & normalize with the normal dhall + tooling, they are standalone (and the types can be instantiated from + dhall as well without any workarounds) +- It can be used like the default `input` function, no injection of + custom symbols in the Normalizer is reqired +- Brings this style of dhall integration to Haskell, where it was only + feasible in nix before, because that is untyped + +The dhall types can be instantiated by every Haskell type that has an +`Interpret` instance. The “name” of the type lambda variable is +compared on the Haskell side with a type-level string that the user +provides, to prevent mixups. + +TODO: +- Improve error messages (!) +- Provide a way to re-use the type mapping on the Haskell side, so + that the returned values are not just the normal `Interpret` types, + but the mapped ones (with name phantom type) +-} +module DhallTypedInput +( inputWithTypeArgs, TypeArg(..), TypeArgError(..), TypeArgEx(..), typeArg +) +where + +import Control.Monad.Trans.State.Strict as State +import Data.List (foldl') +import Control.Exception (Exception) +import qualified Control.Exception +import qualified Data.Text as Text + +import GHC.TypeLits (KnownSymbol, Symbol, symbolVal) +import Data.Proxy (Proxy(Proxy)) + +import Dhall (Type(..), InvalidType(..), InputSettings(..), EvaluateSettings(..), rootDirectory, startingContext, normalizer, standardVersion, sourceName, defaultEvaluateSettings, Interpret(..), auto) +import Dhall.TypeCheck (X) +import Dhall.Core +import Dhall.Parser (Src(..)) +import qualified Dhall.Import +import qualified Dhall.Pretty +import qualified Dhall.TypeCheck +import qualified Dhall.Parser + +import Lens.Family (LensLike', set, view) + +import Data.Text.Prettyprint.Doc (Pretty) +import qualified Data.Text.Prettyprint.Doc as Pretty +import qualified Data.Text.Prettyprint.Doc.Render.Text as Pretty +import qualified Data.Text.Prettyprint.Doc.Render.String as Pretty + + +-- | Information about a type argument in the dhall input +-- +-- If the dhall file starts with @\(CustomType : Type) ->@, +-- that translates to @TypeArg "CustomType" interpretionType@ +-- where @"CustomType"@ is a type-level string describing the +-- name of the type in the dhall file (as a sanity check) and +-- @interpretationType@ is any type which implements +-- 'Dhall.Interpret'. +-- +-- This is basically a specialized 'Data.Proxy'. +data TypeArg (sym :: Symbol) t = TypeArg + +-- | Existential wrapper of a 'TypeArg', allows to create a list +-- of heterogenous 'TypeArg's. +data TypeArgEx + where TypeArgEx :: (KnownSymbol sym, Interpret t) => TypeArg sym t -> TypeArgEx + +-- | Shortcut for creating a 'TypeArgEx'. +-- +-- Use with @TypeApplications@: +-- +-- @ +-- typeArg @"CustomType" @Integer +-- @ +typeArg :: forall sym t. (KnownSymbol sym, Interpret t) => TypeArgEx +typeArg = TypeArgEx (TypeArg :: TypeArg sym t) + +-- | Possible errors returned when applying a 'TypeArg' +-- to a 'Dhall.Expr'. +data TypeArgError + = WrongLabel Text.Text + -- ^ The name (label) of the type was different, + -- the text value is the expected label. + | NoLambda + -- ^ The 'Dhall.Expr' does not start with 'Dhall.Lam'. + +-- | Apply a 'TypeArg' to a 'Dhall.Expr'. +-- +-- Checks that the dhall file starts with the 'Dhall.Lam' +-- corresponding to 'TypeArg`, then applies @t@ (dhall type application) +-- and normalizes, effectively stripping the 'Dhall.Lam'. +applyTypeArg + :: forall sym t. (KnownSymbol sym, Interpret t) + => Expr Src X + -> TypeArg sym t + -> Either TypeArgError (Expr Src X) +applyTypeArg expr ta@(TypeArg) = case expr of + (Lam label (Const Dhall.Core.Type) _) + -> let expectedLabel = getLabel ta + in if label /= getLabel ta + then Left (WrongLabel expectedLabel) + else let expr' = (normalize (App expr tExpect)) + in Right expr' + where + Dhall.Type _ tExpect = Dhall.auto :: Dhall.Type t + expr -> Left NoLambda + +-- | Inflect the type-level string @sym@ to a text value. +getLabel :: forall sym t. (KnownSymbol sym) => TypeArg sym t -> Text.Text +getLabel _ = Text.pack $ symbolVal (Proxy :: (Proxy :: Symbol -> *) sym) + +instance (KnownSymbol sym) => Show (TypeArg sym t) where + show TypeArg = + "TypeArg " + ++ (symbolVal (Proxy :: (Proxy :: Symbol -> *) sym)) + +-- | Takes a list of 'TypeArg's and parses the given +-- dhall string, applying the given 'TypeArg's in order +-- to the opaque dhall type arguments (see 'TypeArg' for +-- how these should look). +-- +-- This is a slightly changed 'Dhall.inputWith'. +-- +-- Discussion: Any trace of our custom type is removed from +-- the resulting +inputWithTypeArgs + :: InputSettings + -> [TypeArgEx] + -> Dhall.Type a + -> Text.Text + -> IO a +inputWithTypeArgs settings typeArgs (Dhall.Type {extract, expected}) txt = do + expr <- throws (Dhall.Parser.exprFromText (view sourceName settings) txt) + + -- TODO: evaluateSettings not exposed + -- let evSettings = view evaluateSettings settings + let evSettings :: EvaluateSettings = defaultEvaluateSettings + + -- -vvv copied verbatim from 'Dhall.inputWith' vvv- + let transform = + set Dhall.Import.standardVersion + (view standardVersion evSettings) + . set Dhall.Import.normalizer + (view normalizer evSettings) + . set Dhall.Import.startingContext + (view startingContext evSettings) + + let status = transform (Dhall.Import.emptyStatus + (view rootDirectory settings)) + + expr' <- State.evalStateT (Dhall.Import.loadWith expr) status + -- -^^^ copied verbatim ^^^- + + let + -- | if there’s a note, run the transformation and rewrap with the note + skipNote e f = case e of + Note n e -> Note n $ f e + e -> f e + + let + -- | strip one 'TypeArg' + stripTypeArg :: Expr Src X -> TypeArgEx -> Expr Src X + stripTypeArg e (TypeArgEx ta) = skipNote e $ \e' -> case e' of + (Lam label _ _) -> + case applyTypeArg e' ta of + Right e'' -> e'' + -- TODO obvously improve error messages + Left (WrongLabel l) -> + error $ "Wrong label, should have been `" ++ Text.unpack l ++ "` but was `" ++ Text.unpack label ++ "`" + Left NoLambda -> error $ "I expected a lambda of the form λ(" ++ Text.unpack label ++ ": Type) → but got: " ++ show e + e' -> error $ show e' + + -- strip all 'TypeArg's + let expr'' = foldl' stripTypeArg expr' typeArgs + + -- -vvv copied verbatim as well (expr' -> expr'') vvv- + let suffix = prettyToStrictText expected + let annot = case expr'' of + Note (Src begin end bytes) _ -> + Note (Src begin end bytes') (Annot expr'' expected) + where + bytes' = bytes <> " : " <> suffix + _ -> + Annot expr'' expected + + _ <- throws (Dhall.TypeCheck.typeWith (view startingContext settings) annot) + case extract (Dhall.Core.normalizeWith (Dhall.Core.getReifiedNormalizer (view normalizer settings)) expr'') of + Just x -> return x + Nothing -> Control.Exception.throwIO InvalidType + + +-- copied from Dhall.Pretty.Internal +prettyToStrictText :: Pretty a => a -> Text.Text +prettyToStrictText = docToStrictText . Pretty.pretty + +-- copied from Dhall.Pretty.Internal +docToStrictText :: Pretty.Doc ann -> Text.Text +docToStrictText = Pretty.renderStrict . Pretty.layoutPretty options + where + options = Pretty.LayoutOptions { Pretty.layoutPageWidth = Pretty.Unbounded } + +-- copied from somewhere in Dhall +throws :: Exception e => Either e a -> IO a +throws (Left e) = Control.Exception.throwIO e +throws (Right r) = return r + + +-- TODO: add errors like these +-- data WrongTypeLabel = WrongTypeLabel deriving (Typeable) + +-- _ERROR :: String +-- _ERROR = "\ESC[1;31mError\ESC[0m" + +-- instance Show WrongTypeLabel where +-- show WrongTypeLabel = +-- _ERROR <> ": Mislabelled type lambda +-- \ \n\ +-- \Expected your t provide an extract function that succeeds if an expression \n\ +-- \matches the expected type. You provided a Type that disobeys this contract \n" |