diff --git a/app/qml/ApplicationSettings.qml b/app/qml/ApplicationSettings.qml index ec77000..2c645f6 100644 --- a/app/qml/ApplicationSettings.qml +++ b/app/qml/ApplicationSettings.qml @@ -51,6 +51,9 @@ QtObject{ // PROFILE SETTINGS /////////////////////////////////////////////////////// + property bool useCustomCommand: false + property string customCommand: "" + property string _backgroundColor: "#000000" property string _fontColor: "#ff8100" property string saturatedColor: Utils.mix(Utils.strToColor("#FFFFFF"), Utils.strToColor(_fontColor), saturationColor * 0.5) @@ -94,6 +97,8 @@ QtObject{ signal terminalFontChanged(string fontSource, int pixelSize, int lineSpacing, real screenScaling, real fontWidth) + signal initializedSettings() + property Loader fontManager: Loader{ states: [ State { when: rasterization == no_rasterization @@ -240,7 +245,9 @@ QtObject{ ambientLight: ambientLight, windowOpacity: windowOpacity, fontName: fontNames[rasterization], - fontWidth: fontWidth + fontWidth: fontWidth, + useCustomCommand: useCustomCommand, + customCommand: customCommand } return settings; } @@ -328,6 +335,9 @@ QtObject{ fontNames[rasterization] = settings.fontName !== undefined ? settings.fontName : fontNames[rasterization]; fontWidth = settings.fontWidth !== undefined ? settings.fontWidth : fontWidth; + useCustomCommand = settings.useCustomCommand !== undefined ? settings.useCustomCommand : useCustomCommand + customCommand = settings.customCommand !== undefined ? settings.customCommand : customCommand + handleFontChanged(); } @@ -377,47 +387,47 @@ QtObject{ property ListModel profilesList: ListModel{ ListElement{ text: "Default Amber" - obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.65,"brightness":0.5,"flickering":0.1,"contrast":0.85,"fontName":"TERMINUS_SCALED","fontColor":"#ff8100","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.2,"horizontalSync":0.16,"jitter":0.18,"burnIn":0.4,"staticNoise":0.1,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0}' + obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.65,"brightness":0.5,"flickering":0.1,"contrast":0.85,"fontName":"TERMINUS_SCALED","fontColor":"#ff8100","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.2,"horizontalSync":0.16,"jitter":0.18,"burnIn":0.4,"staticNoise":0.1,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}' builtin: true } ListElement{ text: "Default Green" - obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.1,"contrast":0.85,"fontName":"TERMINUS_SCALED","fontColor":"#0ccc68","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.2,"horizontalSync":0.16,"jitter":0.18,"burnIn":0.45,"staticNoise":0.1,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0}' + obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.1,"contrast":0.85,"fontName":"TERMINUS_SCALED","fontColor":"#0ccc68","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.2,"horizontalSync":0.16,"jitter":0.18,"burnIn":0.45,"staticNoise":0.1,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}' builtin: true } ListElement{ text: "Default Scanlines" - obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.1,"contrast":0.85,"fontName":"COMMODORE_PET","fontColor":"#00ff5b","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.2,"horizontalSync":0.14,"jitter":0.11,"burnIn":0.4,"staticNoise":0.05,"rasterization":1,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0}' + obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.1,"contrast":0.85,"fontName":"COMMODORE_PET","fontColor":"#00ff5b","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.2,"horizontalSync":0.14,"jitter":0.11,"burnIn":0.4,"staticNoise":0.05,"rasterization":1,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}' builtin: true } ListElement{ text: "Default Pixelated" - obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0,"brightness":0.5,"flickering":0.2,"contrast":0.85,"fontName":"COMMODORE_PET","fontColor":"#ffffff","frameName":"ROUGH_BLACK_FRAME","glowingLine":0.2,"horizontalSync":0.2,"jitter":0,"burnIn":0.45,"staticNoise":0.19,"rasterization":2,"screenCurvature":0.05,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0}' + obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0,"brightness":0.5,"flickering":0.2,"contrast":0.85,"fontName":"COMMODORE_PET","fontColor":"#ffffff","frameName":"ROUGH_BLACK_FRAME","glowingLine":0.2,"horizontalSync":0.2,"jitter":0,"burnIn":0.45,"staticNoise":0.19,"rasterization":2,"screenCurvature":0.05,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}' builtin: true } ListElement{ text: "Apple ][" - obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.5,"brightness":0.5,"flickering":0.2,"contrast":0.85,"fontName":"APPLE_II","fontColor":"#2fff91","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.22,"horizontalSync":0.16,"jitter":0.1,"burnIn":0.65,"staticNoise":0.08,"rasterization":1,"screenCurvature":0.18,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0}' + obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.5,"brightness":0.5,"flickering":0.2,"contrast":0.85,"fontName":"APPLE_II","fontColor":"#2fff91","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.22,"horizontalSync":0.16,"jitter":0.1,"burnIn":0.65,"staticNoise":0.08,"rasterization":1,"screenCurvature":0.18,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}' builtin: true } ListElement{ text: "Vintage" - obj_string: '{"ambientLight":0.5,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.9,"contrast":0.80,"fontName":"COMMODORE_PET","fontColor":"#00ff3e","frameName":"ROUGH_BLACK_FRAME","glowingLine":0.3,"horizontalSync":0.42,"jitter":0.4,"burnIn":0.75,"staticNoise":0.2,"rasterization":1,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0}' + obj_string: '{"ambientLight":0.5,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.9,"contrast":0.80,"fontName":"COMMODORE_PET","fontColor":"#00ff3e","frameName":"ROUGH_BLACK_FRAME","glowingLine":0.3,"horizontalSync":0.42,"jitter":0.4,"burnIn":0.75,"staticNoise":0.2,"rasterization":1,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}' builtin: true } ListElement{ text: "IBM Dos" - obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.07,"contrast":0.85,"fontName":"IBM_DOS","fontColor":"#ffffff","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.13,"horizontalSync":0,"jitter":0.16,"burnIn":0.3,"staticNoise":0.03,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":1,"saturationColor":0,"rbgShift":0.35,"fontWidth":1.0}' + obj_string: '{"ambientLight":0.16,"backgroundColor":"#000000","bloom":0.4,"brightness":0.5,"flickering":0.07,"contrast":0.85,"fontName":"IBM_DOS","fontColor":"#ffffff","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0.13,"horizontalSync":0,"jitter":0.16,"burnIn":0.3,"staticNoise":0.03,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":1,"saturationColor":0,"rbgShift":0.35,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}' builtin: true } ListElement{ text: "IBM 3278" - obj_string: '{"ambientLight":0.1,"backgroundColor":"#000000","bloom":0.15,"brightness":0.5,"flickering":0,"contrast":0.85,"fontName":"IBM_3278","fontColor":"#0ccc68","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0,"horizontalSync":0,"jitter":0,"burnIn":0.6,"staticNoise":0,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0}' + obj_string: '{"ambientLight":0.1,"backgroundColor":"#000000","bloom":0.15,"brightness":0.5,"flickering":0,"contrast":0.85,"fontName":"IBM_3278","fontColor":"#0ccc68","frameName":"SIMPLE_WHITE_FRAME","glowingLine":0,"horizontalSync":0,"jitter":0,"burnIn":0.6,"staticNoise":0,"rasterization":0,"screenCurvature":0.1,"windowOpacity":1,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}' builtin: true } ListElement{ text: "Transparent Green" - obj_string: '{"ambientLight":0.2,"backgroundColor":"#000000","bloom":0.45,"brightness":0.5,"flickering":0.20,"contrast":0.85,"fontName":"TERMINUS_SCALED","fontColor":"#0ccc68","frameName":"NO_FRAME","glowingLine":0.16,"horizontalSync":0.1,"jitter":0.20,"burnIn":0.25,"staticNoise":0.20,"rasterization":0,"screenCurvature":0.05,"windowOpacity":0.60,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0}' + obj_string: '{"ambientLight":0.2,"backgroundColor":"#000000","bloom":0.45,"brightness":0.5,"flickering":0.20,"contrast":0.85,"fontName":"TERMINUS_SCALED","fontColor":"#0ccc68","frameName":"NO_FRAME","glowingLine":0.16,"horizontalSync":0.1,"jitter":0.20,"burnIn":0.25,"staticNoise":0.20,"rasterization":0,"screenCurvature":0.05,"windowOpacity":0.60,"chromaColor":0,"saturationColor":0,"rbgShift":0,"fontWidth":1.0,"useCustomCommand":false,"customCommand":""}' builtin: true } } @@ -455,6 +465,8 @@ QtObject{ fullscreen = true; showMenubar = false; } + + initializedSettings(); } Component.onDestruction: { storeSettings(); diff --git a/app/qml/PreprocessedTerminal.qml b/app/qml/PreprocessedTerminal.qml index f1ecfdb..76803f4 100644 --- a/app/qml/PreprocessedTerminal.qml +++ b/app/qml/PreprocessedTerminal.qml @@ -129,13 +129,17 @@ Item{ kterminal.lineSpacing = lineSpacing; } - Component.onCompleted: { - appSettings.terminalFontChanged.connect(handleFontChange); + function startSession() { + appSettings.initializedSettings.disconnect(startSession); // Retrieve the variable set in main.cpp if arguments are passed. if (defaultCmd) { ksession.setShellProgram(defaultCmd); ksession.setArgs(defaultCmdArgs); + } else if (appSettings.useCustomCommand) { + var args = Utils.tokenizeCommandLine(appSettings.customCommand); + ksession.setShellProgram(args[0]); + ksession.setArgs(args.slice(1)); } else if (!defaultCmd && Qt.platform.os === "osx") { // OSX Requires the following default parameters for auto login. ksession.setArgs(["-i", "-l"]); @@ -147,6 +151,10 @@ Item{ ksession.startShellProgram(); forceActiveFocus(); } + Component.onCompleted: { + appSettings.terminalFontChanged.connect(handleFontChange); + appSettings.initializedSettings.connect(startSession); + } } Component { id: linuxContextMenu diff --git a/app/qml/SettingsGeneralTab.qml b/app/qml/SettingsGeneralTab.qml index 18f72a6..5c10dd9 100644 --- a/app/qml/SettingsGeneralTab.qml +++ b/app/qml/SettingsGeneralTab.qml @@ -159,6 +159,40 @@ Tab{ } } } + + GroupBox{ + anchors {left: parent.left; right: parent.right} + title: qsTr("Command") + ColumnLayout { + anchors.fill: parent + CheckBox{ + id: useCustomCommand + text: qsTr("Use custom command instead of shell at startup") + checked: appSettings.useCustomCommand + onCheckedChanged: appSettings.useCustomCommand = checked + } + // Workaround for QTBUG-31627 for pre 5.3.0 + Binding{ + target: useCustomCommand + property: "checked" + value: appSettings.useCustomCommand + } + TextField{ + id: customCommand + anchors {left: parent.left; right: parent.right} + text: appSettings.customCommand + enabled: useCustomCommand.checked + onEditingFinished: appSettings.customCommand = text + + // Save text even if user forgets to press enter or unfocus + function saveSetting() { + appSettings.customCommand = text; + } + Component.onCompleted: settings_window.closing.connect(saveSetting) + } + } + } + // DIALOGS //////////////////////////////////////////////////////////////// InsertNameDialog{ id: insertname diff --git a/app/qml/utils.js b/app/qml/utils.js index ba8ae04..a8e1af0 100644 --- a/app/qml/utils.js +++ b/app/qml/utils.js @@ -21,3 +21,74 @@ function strToColor(s){ var b = parseInt(s.substring(5,7), 16) / 256; return Qt.rgba(r, g, b, 1.0); } + +/* Tokenizes a command into program and arguments, taking into account quoted + * strings and backslashes. + * Based on GLib's tokenizer, used by Gnome Terminal + */ +function tokenizeCommandLine(s){ + var args = []; + var currentToken = ""; + var quoteChar = ""; + var escaped = false; + var nextToken = function() { + args.push(currentToken); + currentToken = ""; + } + var appendToCurrentToken = function(c) { + currentToken += c; + } + + for (var i = 0; i < s.length; i++) { + + // char followed by backslash, append literally + if (escaped) { + escaped = false; + appendToCurrentToken(s[i]); + + // char inside quotes, either close or append + } else if (quoteChar) { + escaped = s[i] === '\\'; + if (quoteChar === s[i]) { + quoteChar = ""; + nextToken(); + } else if (!escaped) { + appendToCurrentToken(s[i]); + } + + // regular char + } else { + escaped = s[i] === '\\'; + switch (s[i]) { + case '\\': + // begin escape + break; + case '\n': + // newlines always delimits + nextToken(); + break; + case ' ': + case '\t': + // delimit on new whitespace + if (currentToken) { + nextToken(); + } + break; + case '\'': + case '"': + // begin quoted section + quoteChar = s[i]; + break; + default: + appendToCurrentToken(s[i]); + } + } + } + + // ignore last token if broken quotes/backslash + if (currentToken && !escaped && !quoteChar) { + nextToken(); + } + + return args; +}