<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
<!--}}}-->
Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
/*{{{*/
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}

a {color:[[ColorPalette::PrimaryMid]];}
a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
a img {border:0;}

h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}

.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}

.header {background:[[ColorPalette::PrimaryMid]];}
.headerShadow {color:[[ColorPalette::Foreground]];}
.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
.headerForeground {color:[[ColorPalette::Background]];}
.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}

.tabSelected {color:[[ColorPalette::PrimaryDark]];
	background:[[ColorPalette::TertiaryPale]];
	border-left:1px solid [[ColorPalette::TertiaryLight]];
	border-top:1px solid [[ColorPalette::TertiaryLight]];
	border-right:1px solid [[ColorPalette::TertiaryLight]];
}
.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
.tabContents .button {border:0;}

#sidebar {}
#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}

.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
	border:1px solid [[ColorPalette::PrimaryMid]];}
.wizardStep.wizardStepDone {background:[[ColorPalette::TertiaryLight]];}
.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}

.wizard .notChanged {background:transparent;}
.wizard .changedLocally {background:#80ff80;}
.wizard .changedServer {background:#8080ff;}
.wizard .changedBoth {background:#ff8080;}
.wizard .notFound {background:#ffff80;}
.wizard .putToServer {background:#ff80ff;}
.wizard .gotFromServer {background:#80ffff;}

#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}

.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}

.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}

.tiddler .defaultCommand {font-weight:bold;}

.shadow .title {color:[[ColorPalette::TertiaryDark]];}

.title {color:[[ColorPalette::SecondaryDark]];}
.subtitle {color:[[ColorPalette::TertiaryDark]];}

.toolbar {color:[[ColorPalette::PrimaryMid]];}
.toolbar a {color:[[ColorPalette::TertiaryLight]];}
.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}

.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
.tagging .button, .tagged .button {border:none;}

.footer {color:[[ColorPalette::TertiaryLight]];}
.selected .footer {color:[[ColorPalette::TertiaryMid]];}

.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}

.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}

.imageLink, #displayArea .imageLink {background:transparent;}

.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}

.viewer .listTitle {list-style-type:none; margin-left:-2em;}
.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}

.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}

.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
.viewer code {color:[[ColorPalette::SecondaryDark]];}
.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}

.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}

.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
.editorFooter {color:[[ColorPalette::TertiaryMid]];}
.readOnly {background:[[ColorPalette::TertiaryPale]];}

#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:alpha(opacity=60);}
/*}}}*/
/*{{{*/
* html .tiddler {height:1%;}

body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}

h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}

.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}

/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}

#mainMenu .tiddlyLinkExisting,
	#mainMenu .tiddlyLinkNonExisting,
	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}

.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0 1em 1em; left:0; top:0;}

.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}

#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}

#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 0.3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}

.wizard {padding:0.1em 1em 0 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0; margin:0.4em 0 0.2em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0; margin:0.4em 0 0.2em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0 0; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0;}
.wizardFooter .status {padding:0 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em;}

#messageArea {position:fixed; top:2em; right:0; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em;}
#messageArea a {text-decoration:underline;}

.tiddlerPopupButton {padding:0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em; margin:0;}

.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}

.tabset {padding:1em 0 0 0.5em;}
.tab {margin:0 0 0 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}

#contentWrapper {display:block;}
#splashScreen {display:none;}

#displayArea {margin:1em 17em 0 14em;}

.toolbar {text-align:right; font-size:.9em;}

.tiddler {padding:1em 1em 0;}

.missing .viewer,.missing .title {font-style:italic;}

.title {font-size:1.6em; font-weight:bold;}

.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}

.tiddler .button {padding:0.2em 0.4em;}

.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}

.footer {font-size:.9em;}
.footer li {display:inline;}

.annotation {padding:0.5em; margin:0.5em;}

* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0 0.25em; padding:0 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}

.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0 3px 0 3px;}

.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}

.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0; font-size:.9em;}
.editorFooter .button {padding-top:0; padding-bottom:0;}

.fieldsetFix {border:0; padding:0; margin:1px 0px;}

.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}

* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0; right:0;}
#backstageButton a {padding:0.1em 0.4em; margin:0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; width:90%; margin-left:3em; padding:1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}

.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
/***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
***/
/*{{{*/
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
/*}}}*/
/*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none !important;}
#displayArea {margin: 1em 1em 0em;}
noscript {display:none;} /* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
}
/*}}}*/
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser excludeLists'></span></div>
<!--}}}-->
To get started with this blank [[TiddlyWiki]], you'll need to modify the following tiddlers:
* [[SiteTitle]] & [[SiteSubtitle]]: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* [[MainMenu]]: The menu (usually on the left)
* [[DefaultTiddlers]]: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
These [[InterfaceOptions]] for customising [[TiddlyWiki]] are saved in your browser

Your username for signing your edits. Write it as a [[WikiWord]] (eg [[JoeBloggs]])

<<option txtUserName>>
<<option chkSaveBackups>> [[SaveBackups]]
<<option chkAutoSave>> [[AutoSave]]
<<option chkRegExpSearch>> [[RegExpSearch]]
<<option chkCaseSensitiveSearch>> [[CaseSensitiveSearch]]
<<option chkAnimate>> [[EnableAnimations]]

----
Also see [[AdvancedOptions]]
<<importTiddlers>>
The CTC Flashforge does a good job of dual extrusion from slic3r without having to build fancy flushing towers etc but you do need to get the retraction just right. After many experiments here is what works for me (slic3r settings)
Printer Settings -> Extruder 1/2 -> 
*Nozzle diameter 0.4
*X/Y Offsets 0
*Retraction length 0.5
*Lift Z 0
*Speed 40
*Extra length 0
*Minimum travel 2
*Retract on layer change yes
*Wipe while retracting yes (with above setting forces a wipe on layer change which avoids holes at the seam)
*Retraction when tool is disabled: 15 (Extra length on restart 0)
See [[Calibrating a Replicator 1 dual with Sailfish firmware]] for custom G-code
This is quite hard! The steps are:
#Produce a pdf with the same number of pages as the input file containing just the page numbers
##Write a script to produce a simple PS file and then convert it using ps2pdf
##You can use {{{pdfinfo -f 1 -l 999 | grep "Page.*size"}}} to find how many pages are in the doc and which pages are landscape / portrait
#Use the page numbers file as a background for the input file using pdftk's multibackground option:
##{{{pdftk input.pdf multibackground page-numbers.pdf output numbered.pdf}}}

Here's the resulting script:
{{{
#!/bin/bash
#GUI for adding page numbers to a pdf file

# File to store the previously accessed document path
RCFILE=$HOME/.pdf-pagenums

if ! which pdftk >/dev/null; then
  zenity --error --text="Error: pdftk not installed"
  exit 1
fi

# Select the input pdf
if [ -r $RCFILE ]; then # start from where we were last time
  START="$(cat $RCFILE)"
else
  START="$(pwd)/"
fi
while true; do
  PDF=$(zenity --file-selection --filename="$START" --title="Open PDF document")
  if [ -z "$PDF" ]; then exit 1; fi # Canceled
  if file "$PDF" | grep "PDF document" >/dev/null ; then break; fi # It's a valid PDF
done

# Store as starting place for the next run
echo "$PDF" > $RCFILE

TMPF="pdf-pagenums-tmp.ps" # Write a PS file header
echo "%!PS" >$TMPF
echo "/Times-Roman findfont" >>$TMPF
echo "12 scalefont" >>$TMPF
echo "setfont" >>$TMPF
echo "newpath" >>$TMPF

pdfinfo "$PDF" -f 1 -l 999 | # get info about each page and build the PS pages accordingly
  grep "Page.*size" | 
  while read T TPageN T TSize1 T TSize2 T; do
	echo "%%Page: $TPageN $TPageN" >>$TMPF
	echo "%%BeginSetup" >>$TMPF
	echo "  << /PageSize [$TSize1 $TSize2] /Orientation 0 >> setpagedevice" >>$TMPF
	echo "%%EndSetup" >>$TMPF
	echo "20 20 moveto" >>$TMPF
	echo "(Page $TPageN) show" >>$TMPF
	echo "showpage" >>$TMPF
done 
# Choose destination
OUTF=$(zenity --file-selection --filename="${PDF%.*}-numbered.pdf" --save --title="Save PDF with page numbers")

if [ -z "$OUTF" ]; then 
  exit 1
fi

# Translate the PS into a PDF for pdftk
ps2pdf $TMPF $TMPF.pdf
# Combine the page numbers with the original document
if ! pdftk "$PDF" multibackground $TMPF.pdf output "$OUTF" ; then
  zenity --error --text="Failed"
  exit 1
fi

# Tidy up temp files if it all worked OK
rm $TMPF
rm $TMPF.pdf
}}}
Before closing old ROM:
#Backup launcher settings in launcher
#Backup user apps and data in TB, delete unused backups
#Backup WiFi APNs in TB
#Reboot into recovery
#Backup ROM
#Wipe everything
#Install new ROM
#Reboot
#(poss enable a2sd and reboot)

Install apps:
#Change date time to 24 hour
#Sign in to Google Play
#Install Titanium backup and pro from market
#Change device ID in TB and reboot
#Install missing apps in TB
#Recover WiFi APNs
#Set Business Calendar widgets (2x3)

Set security:
#Set unlock pattern
#Set Cerebus, sign in, activate, 

General:
#Start Day week bar
#Start Usage Timelines
#Check Timeriffic, Volume Locker, SMS backup
#Re-instate home screen widgets if necessary
Make an entry in the sudoers file ({{{/etc/sudoers}}} or a file in {{{/etc/sudoers.d}}}) like this:
{{{
%sudo ALL=(root) NOPASSWD: /full/path/to/command
}}}
The {{{%sudo}}} bit means anyone in the sudoers group runs the command as root {{{(root)}}}.
!!!General
To see the catlog which shows errors running your app: in Eclipse go to Window / Show View / Other / Android / Logcat.
http://developer.android.com/reference/packages.html - developers reference
http://ucla.jamesyxu.com/?p=285 - stuff about memory leaks and weakreference and static classes
!!!Time and Date
http://developer.android.com/reference/android/os/SystemClock.html - system clock and sleep
http://developer.android.com/reference/java/text/DateFormat.html - Date format conversions
http://developer.android.com/reference/java/text/SimpleDateFormat.html - Date format strings (note H, K, L don't work on older Androids, use k, h, M instead respectively)
!!!Preferences
http://developer.android.com/design/patterns/settings.html, http://developer.android.com/guide/topics/ui/settings.html - developer's guides for laying out settings dialogs
http://developer.android.com/reference/android/preference/Preference.html - preferences
http://developer.android.com/training/basics/data-storage/shared-preferences.html - saving preferences
http://developer.android.com/reference/android/preference/PreferenceActivity.html#addPreferencesFromResource%28int%29 - preference activity
http://jetpad.org/2011/01/creating-a-preference-activity-in-android/
!!!Problems and solutions
Preference fragment overlays instead of replaces main screen: mixing pre Honeycomb and post HC calls, remove compat includes
"""ActionBarActivity""" not in post HC: use Activity instead and make sure you have {{{android:showAsAction}}} in your menu xml NOT {{{app:showAsAction}}}
Fragment can't have an activity as a constructor parameter and is static: implement onAttach which passes in the activity and store in a class global
https://forum.xda-developers.com/google-nexus-5/help/battery-life-help-troubleshoot-battery-t2785128
Archive of older entries is at http://peterthevicar-archive.tiddlyspot.com
Following the {{{Getting Started}}} instructions on the Arduino Create website https://create.arduino.cc installed the helper program faultlessly. There was no need for any other installations. If you get a permissions error accessing the USB device, add your user to the dialout group and log out and back in again. The permissions on /dev/ttyUSBn were tricky on one computer where even adding the user to dialout group didn't work. For that one I had to {{{chown user:dialout /dev/ttyUSBn}}} each time. Once or twice the USB connection got in a knot and I had to restart the computer.
There are two related ratios:
#Display aspect ratio (DAR or AR) which is the ratio of width to height of the actual display. This has nothing to do with the resolution or pixel shape.
#Pixel aspect ratio (PAR) which is the ratio of width to height of EACH PIXEL.

In other words if you have 10x10 pixels, each with a PAR of 4/3 (1.33) then the display will have an AR of 10*4 / 10*3 (1.33) too. if you have 40 x 30 of the same shaped pixels your AR will be 40*4 / 30*3 = 16/9 (1.78). 

The norm for PAL broadcast is PAR of 64/45 with a resolution of 720x576. That gives a DAR of 720*64 / 576*45 = 1.78 (16/9)

Here is the CSV source for a spreadsheet to calculate good values for a 1:1 output file:
{{{
Wsrc,Hsrc,DARsrc,PARsrc,TRY,Wdest,Hcalc,Hdest,Herr %
720,576,=16/9,='Wsrc'/'Hsrc'*'DARsrc',90,='TRY'*8,='Wdest'/'DARsrc',"=MROUND('Hcalc',8)","=ROUND(('Hdest'-'Hcalc')/'Hcalc' *100,2)"
}}}
(The results for these values were: 90,720,405,408,0.74, i.e. use 720x408 for the output)
Enter the values for the width, height and DAR of the source (see [[Finding information about video clips]]), then move TRY up and down to get the smallest Herr.
Get this message when inactive in Xubuntu long enough to suspend. Trouble is it prevents the suspend because noone is there to authorise it. Solution is to edit {{{/usr/share/polkit-1/actions/org.freedesktop.login1.policy}}} and change {{{auth_admin_keep}}} to {{{yes}}} in:
{{{
       <action id="org.freedesktop.login1.suspend">
                <description>Suspend the system</description>
                <message>Authentication is required for suspending the system.</message>
                <defaults>
                        <allow_any>yes</allow_any>
                        <allow_inactive>yes</allow_inactive>
                        <allow_active>yes</allow_active>
                </defaults>
        </action>
}}}
To get the same effect as clicking on the drive in Thunar: {{{udisksctl mount -b /dev/disk/by-label/Backup}}}
Could put that in .xsessionrc or /etc/init.d etc but would also need it in /lib/systemd/system-sleep/ for resume
This /etc/fstab entry will attempt the mount when the disk is first accessed:
{{{LABEL=Backup /Disks/Backup ext4 rw,noatime,noauto,x-systemd.automount 0 2}}}
This /etc/fstab entry will attempt the mount when the disk at boot but not worry if it's not there:
{{{LABEL=Backup /Disks/Backup ext4 rw,noatime,auto,nofail 0 2}}}

Also see [[Doing something after a resume from suspend]] as I found I had to manually remount after resume
use {{{exiftran -ia <image-file>}}} to automatically rotate in place according to the EXIF tags
See http://peterthevicar-archive.tiddlyspot.com/#%5B%5BBash%20parameters%20and%20substitution%5D%5D for full list of parameter stuff in bash, but these are some nice, simple uses:
{{{FOO=${1:-"bar"} # FOO will be $1 or, if $1 is empty, "bar"}}}
{{{
FN=$(basename $pathandfile)
${FN%.*} - returns the filename without the type (final . and following chars)
${FN##*.} - gives the type only
}}}

{{{
for x in {1..5}; do echo $i; done
1
2
3
4
5
}}}

{{{
$ numfmt --to=si 1000
1K
$ numfmt --from=si 1K
1000
}}}
From http://reprap.org/wiki/Triffid_Hunter%27s_Calibration_Guide

Best results are obtained when layer height < 80% of nozzle diameter, and extrusion width >= nozzle diameter.

For 0.4mm that gives:
|Nozzle|Layer height|Extrusion width|
| 0.4 | < 0.32 | >= 0.4 |
When viewing a pdf saved by """LibreOffice""" Adobe Reader shows the colours OK but then prints them very dull. Presumably Adobe Reader is trying to do something clever with colour management and failing spectacularly. There are two ways round this:
#Use the Ubuntu Document Viewer which prints fine.
#In Adobe Reader's print dialog, click the Advanced button and tick the option 'Let printer determine colours'. My printer certainly does a better job than AR!
Had a NUC which stopped booting (UEFI). I did this and it started working again:
{{{
sudo add-apt-repository ppa:yannubuntu/boot-repair
sudo apt-get update
sudo apt-get install -y boot-repair && boot-repair
}}}
#Download the build system
#{{{make menuconfig}}} and tick the image builder
#{{{make}}}
#Go to bin/targets/....
#Unpack the image builder and cd into it
#In the image builder directory: {{{make info | grep -A10 615}}}
#{{{make image PROFILE=dlink_dir-615-d PACKAGES="uhttpd uhttpd-mod-ubus libiwinfo-lua luci-base luci-app-firewall luci-mod-admin-full luci-theme-bootstrap -ppp -ppp-mod-pppoe -iptables -ip6tables -odhcp6c -kmod-ipv6 -kmod-ip6tables -odhcpd-ipv6only -odhcpd -opkg"}}}
If you can't use """LuCi""" to install the new image:
#{{{scp bin/targets/ramips/rt305x/openwrt-ramips-rt305x-dlink_dir-615-d-squashfs-sysupgrade.bin root@192.168.1.1:/tmp}}}
#{{{ssh root@192.168.1.1}}}
#{{{sysupgrade -v -n /tmp/*.bin}}}

!!Set up Android Studio
Note: most of the disk space used is in android-sdk, android-studio and """StudioProjects""" dirs. You can put these where you like. The system also creates a fairly small dir in your home dir: {{{.AndroidStudio4.0}}} and two pretty large ones: {{{.android .gradle}}} If disk space is short you can move it to the spare drive and link it. In particular if you use an emulated phone it is stored in {{{.android}}} and needs lots of spare disk to run or you get the error {{{Emulator: emulator: ERROR: Not enough disk space to run AVD 'Pixel_API_30'. Exiting...}}} when you try to run the app
#Download Studio from https://developer.android.com/studio
#Unpack the file into large scratch directory, e.g /Disks/Spare/Android-appdev/ it will create a subdir android-studio
#Install git ({{{apt list git}}} will tell you if it's already installed)
#To launch Android Studio, navigate to the {{{android-studio}}} directory in a terminal and execute {{{bash bin/studio.sh}}}
!!First run of Android Studio
#Say you want a new setup, no import of previous settings
#Select 'Custom' option in the 'Install Type' page
##Set sdk directory to same scratch drive, eg /Disks/Spare/Android-appdev/android-sdk
#KVM optimisation details are found at http://developer.android.com/tools/devices/emulator.html#vm-linux - works only for x86 and helps with emulations rather than builds
#Start the download (about 700MB). May get some 'unknown host' warnings as it searches for mirrors.
#Choose 'Checkout project from Version Control' pull down and select git
##URL is https://github.com/peterthevicar/dementiadayclock
##Set """StudioProjects""" directory on same scratch drive, e.g. {{{/Disks/Spare/Android-appdev/android-StudioProjects}}}
##Hit clone and wait for download
##Say yes when it asks if you want to open the gradle project file
##File/Sync Project with Gradle Files
##Probably throw up a few version errors, go to File/Project Structure to choose the latest versions:
###Go go to {{{File/ProjectStructure}}} and then the app below the Modules line on the left
###Check the compile SDK and build tools versions
#Go to {{{Tools/Android/SDK}}} Manager and make sure the exact matching versions are installed
#Build the project (Build / Make Project)
#Run the app:
##Note: couldn't get this to work on virtual device so plugged in my real phone instead. Need to make sure USB debugging is enabled in Developer Options of the phone.
##Go to Tools/AVD Manager (Android Virtual Device) and clone a likely looking phone. For me the Nexus 5X with Orio (API 27) worked OK, R didn't
##This will download and then unzip over a GB of android OS so be patient
#Assuming it works (!!) update the versions in Project Structure to the latest available (should have been installed in the initial setup of AS)
!!Building and uploading a new APK
#Change the version numbers in Manifest.xml
#Change the "About, changes" entry in Strings.xml
#Update Help text in Strings.xml
#{{{Build/Signed APK}}}
#Keystore stored in git, comes out in {{{...StudioProjects/keystore}}}, keystore passwords same (s.n), alias salisburys.net
#Choose both signatures
#Move APK from app/release directory, renaming with version into /Release directory
#Check VCS updates and add the new release APK
#{{{VCS/Commit (Ctrl-K)}}}
#{{{VCS/Git/Push (Ctrl-Shift-K)}}}
##github, username (p.v), password (github)
#Log in to github and publish a new release https://github.com/peterthevicar/dementiadayclock/releases
#Different browser go to Android Developer Console as salisburys.net https://play.google.com/apps/publish/signup/
#Change the Market info and upload new screenshots
#Upload new APK and fill in changelog
(from http://www.sailfishfirmware.com/doc/tuning-slicer-calibration.html)
gcode summary is at: http://reprap.org/wiki/G-code
sailfish setup is at: http://www.makerbot.com/sailfish/setup/

Important things are:
#Custom gcode for Replicator 1 to go into slic3r Printer Settings (see below)
#Filament diameter
##Measure the filament many times and take the average
#Filament feed rate
##Print the filament vernier or work out another way of measuring the input filament very accurately
##print the gcode for feeding exactly 10mm of filament (see below) and see how it compares
##adjust the feed rate multiplier in slic3r to compensate for over/under feeding
#Extrusion temperature
##print a high tower with 0% infill and one perimiter, adjusting the temperature manually as it goes up
##the correct temperature will be pretty obvious +/- a couple of degrees
##if the first layer has to be a bit hotter to get it to stick slic3r can handle that for you

Custom G-code is complicated by the fact that everything has to be passed through the gpx pre-processor. This is complicated for two reasons: firstly it doesn't exactly follow the g-code standard and secondly it doesn't understand the output from slic3r so you have to pre-pre-process it using a script:
{{{
#!/bin/bash
# Process a gcode file to create a g3x file for the Replicator

if [ ! -f "$1" ]; then
  echo "usage: $0 filename"
fi

#Confirm gcode file
echo "G-code file"
ls -l $1

# Massage the file into a form suitable for gpx
BASF=${1%.*}
INTF="$BASF.gpx-gcode"
cat $1 |
  sed "s/;.*//" | # Strip the comments
  sed "s/^M108 T[01]$//" | # Take out the default tool change gcode which gpx doesn't like
  grep -v "^M70 P[0-9]*$" | # Remove M70 display commands which have no text specified
  grep -v "^$" >$INTF # remove blank lines
  
# Run gpx for Replicator 1 dual
gpx -g -m r1d $INTF $BASF.x3g
# Report
echo -en "G-code file:  $1\nModified:  $(date -r $1)\n\nOutput:  $(du -h $BASF.x3g)$(grep "filament used" $1)" |
zenity --text-info \
  --title "Summary" \
  --height=200 --width=600
}}}
Custom Start G-code for slic3r for single extruder prints
{{{
;===Begin start gcode
M73 P0; enable show build progress
M320; acceleration enabled for all commands that follow
G90; absolute co-ordinates
;---Heat up
M109 S[first_layer_bed_temperature]  T0 ; heat bed up to first layer temperature
M104 S[first_layer_temperature_0] T0 ; set T0 (right) heater to first layer temperature
;---Homing
T0
G162 X Y F9000; home XY axes maximum
;---Careful Z axis homing
G161 Z F6000; home Z axis minimum
G92 Z-3; prepare for small move up
G1 Z0.0 F900; move up
G161 Z F100; re-home very slowly
;---Set co-ordinate system
M132 X Y Z A B; Recall stored home offsets (can change on printer)
;---Go to waiting position (T0)
G1 X-115 Y-74 Z[first_layer_height] F9000.0; move to waiting position (front left corner of platform)
;---Warm up time
M6 T0; wait for bed and T0 to heat up
;---Priming extrusion T0
T0; change to T0
G92 E0; set E to 0
G1 X100 Y-75 Z[first_layer_height] F9000.000; move to front right corner of bed
G1 X-80 Y-75 E15 F1500.000; extrude a line of filament across the front edge of the bed
G92 E0; set E to 0
G4 P1000; wait for ooze to stop
;===End of start gcode
}}}
Custom start gcode for Dual extruder prints:
{{{
;===Begin start gcode
M73 P0; enable show build progress
M320; acceleration enabled for all commands that follow
G90; absolute co-ordinates
;---Heat up
M109 S[first_layer_bed_temperature]  T0 ; heat bed up to first layer temperature
M104 S[first_layer_temperature_0] T0 ; set T0 (right) heater to first layer temperature
M104 S[first_layer_temperature_1] T1 ; set T1 (left) heater to first layer temperature
;---Homing
T0
G162 X Y F9000; home XY axes maximum
;---Careful Z axis homing
G161 Z F6000; home Z axis minimum
G92 Z-3; prepare for small move up
G1 Z0.0 F900; move up
G161 Z F100; re-home very slowly
;---Set co-ordinate system
M132 X Y Z A B; Recall stored home offsets (can change on printer)
;---Go to waiting position (T0)
G1 X-115 Y-74 Z[first_layer_height] F9000.0; move to waiting position (front left corner of platform)
;---Warm up time
M6 T0; wait for bed and T0 to heat up
M6 T1; wait for T1 to heat up
;---Priming extrusion T1
T1; change to T1
G92 E0; set E to 0
G1 X100 Y-74 Z[first_layer_height] F9000.000; move to front right corner of bed
G1 X-80 Y-74 E15 F1500.000; extrude a line of filament across the front edge of the bed
G92 E0; set E to 0
G4 P1000; wait for ooze to stop
;---Priming extrusion T0
T0; change to T0
G92 E0; set E to 0
G1 X100 Y-75 Z[first_layer_height] F9000.000; move to front right corner of bed
G1 X-80 Y-75 E15 F1500.000; extrude a line of filament across the front edge of the bed
G92 E0; set E to 0
G4 P1000; wait for ooze to stop
;===End of start gcode
}}}
End G-code
{{{
M73 P100 ; end build progress
G162 X Y F2500; home X and Y axes
M109 S0 T0; set bed temperature to 0
M104 S0 T0; set 1st extruder temperature to 0
M104 S0 T1; set 2nd extruder temperature to 0
M18; disable all stepper motors
M72 P2; Alert
}}}
Tool change G-code - not needed
G-code for extruding 10mm of input filament (about 40mm of output filament)
{{{
; Hand written gcode to extrude 10mm filament from primary nozzle at 250 degrees
; Used for calibration

; First get everything in the right place and heated up
M73 P0; set build progress to 0
T0; set primary extruder
M104 S250 T0 ; set nozzle temperature
G162 X Y F9000; home XY axes maximum
G161 Z F6000; home Z axis minimum
G92 Z0 E0; set Z and E to 0
G1 Z10 F1200; move bed out of the way
M104 S250 T0 ; set nozzle temperature
M6 T0; wait for extruder to heat up
;
; All ready to go - do it!
;
G1 F180 E10; extrude 10mm of filament at 3mm/s
;
;
M104 S0 T0; set 1st extruder temperature to 0
M18; disable all stepper motors
M70 P5 (Extruded 10mm)
M73 P100; set build progress to 100
}}}
|Format|Result|Markup|h
|Bold|''text''|{{{''text''}}}|
|Italic|//text//|{{{//text//}}}|
|Underline|__text__|{{{__text__}}}|
|Strike-through|--text--|{{{--text--}}}|
|Colour|@@color(green):text@@|{{{@@color(green):text@@}}}|
|Background|@@bgcolor(#ff0000):color(#ffffff):text@@|{{{@@bgcolor(#ff0000):color(#ffffff):text@@}}}|
|Highlight|@@text@@|{{{@@text@@}}}|
|Any CSS|e.g. @@text-decoration:overline;text@@|{{{e.g. @@text-decoration:overline;text@@}}}|
|Superscript|text^^text2^^|{{{text^^text2^^}}}|
|Subscript|text~~text2~~|{{{text~~text2~~}}}|
|Monospace|{{{text}}}|<html><code>{{{text}}}</code></html>|
|~|~|{{{<html><code>text</code></html>}}}|
|nowiki|"""WikiWord"""|{{{"""WikiWord"""}}}|
|~|~|{{{<nowiki>WikiWord</nowiki>}}}|
|CSS <span>||<html><code>{{myclass{text}}}</code></html>*|
|CSS <div>||<html><code>{{myclass{<br>text}}}</code></html>*|
"""*"""You must also add {{{.myclass}}} to StyleSheet
#YAGBE - access to google bookmarks
Complex stuff, need a monitor calibration instrument.
See http://www.russellcottrell.com/photo/LinuxWorkflow.htm
Press {{{Shift-AltGr}}} then two characters, e.g. {{{Shift-AltGr}}} then {{{' e}}} gives {{{é}}}. Most also work with the accent after the character (e.g. e') but generally the modifier comes first. Most accents are obvious: comma for cedilla, double quote for umlaut etc. {{{Shift-AltGr oA}}} gives Å. For a pretty full list see https://help.ubuntu.com/community/GtkComposeTable or read {{{/usr/share/X11/locale/en_US.UTF-8/Compose}}}
|!Accents|{{{'e}}}→é {{{`e}}}→è {{{^u}}}→û {{{"u}}}→ü {{{oa}}}→å {{{ca}}}→ǎ {{{,c}}}→ç {{{.a}}}→ȧ {{{/l}}}→ł {{{-u}}}→ū|
|!Fractions|{{{12}}}→½ {{{13}}} etc→⅓¼⅕⅙⅐⅛⅑ {{{110}}}→⅒ {{{23}}} etc→⅔⅖¾⅗⅜⅘⅝⅞|
|!Symbols|{{{tm}}}→™ {{{oc}}}→© {{{or}}}→® {{{oo}}}→° {{{p!}}}→¶ {{{/u}}}→µ|
|!Arrows|{{{<-}}}→← {{{->}}}→→|
|!Currency|{{{=e}}}→€ {{{=y}}}→¥ {{{/c}}}→¢|
|!Maths|{{{+-}}}→± {{{xx}}}→× {{{^.}}}→· {{{:-}}}→÷ {{{>=}}}→≥ {{{<=}}}→≤ {{{/=}}}→≠ {{{.:}}}→∵ {{{:.}}}→∴ {{{-,}}}→¬|
|!Punctuation|{{{..}}}→… {{{---}}}→— {{{--.}}}→–|
|!Quotes|{{{<"}}}→“ {{{>"}}}→” {{{<'}}}→‘ {{{>'}}}→’ {{{<<}}}→« {{{>>}}}→»|
|!Superscript numbers|{{{^1}}} etc→¹²³⁴⁵⁶⁷⁸⁹⁰|
|!Music|{{{##}}}→♯ {{{#b}}}→♭ {{{#f}}}→♮ {{{#q}}}→♩ {{{#E}}}→♫ {{{#e}}}→♪ {{{#S}}}→♬|


Also you can use {{{AltGr}}} and {{{AltGr-Shift}}} (note press Shift afterwards) to get lots of special characters. Use {{{xkbprint :0 -label symbols -ll 3 -o kbmap.ps}}} to see them all. Here are a few. When it says something like e=é that means pressing {{{AltGr-;}}} followed by e gives é.

|Shift|¬|"""!"""|"|£|$|%|^|&|*|(|)|_|+|h
|None|`|1|2|3|4|5|6|7|8|9|0|-|=|h
|{{{AltGr}}}|"""|"""|¹|²|³|€|½|¾|{|[|]|}|\|c=ç|
|{{{AltGr-Shift}}}|"""|"""|¡|⅛|£|¼|⅜|⅝|⅞|™|±|°|¿|e=ę|
|None||q|w|e|r|t|y|u|i|o|p|[|]|h
|{{{AltGr}}}||@|ł|e|¶|ŧ|←|↓|→|ø|þ|e=ë|e=ẽ|
|{{{AltGr-Shift}}}||Ω|Ł|E|®|Ŧ|¥|↑|ı|Ø|Þ|a=å|a=ā|
|None|||a|s|d|f|g|h|j|k|l|;|'|#|h
|{{{AltGr}}}|||æ|ß|ð|đ|ŋ|ħ|a=ả|ĸ|ł|e=é|e=ê|e=è|
|{{{AltGr-Shift}}}|||Æ|§|Ð|ª|Ŋ|Ħ|u=ư|&|Ł|u=ű|a=ǎ|a=ă|
|None||\|z|x|c|v|b|n|m|,|.|/|h
|{{{AltGr}}}||"""|"""|«|»|¢|“|”|n|µ||·|a=ạ|
|{{{AltGr-Shift}}}||¦|<|>|©|‘|’|N|º|×|÷|a=ȧ|
Want: develop a 2-way communication between headless """RPi""" and Arduino. The """RPi""" is too slow and needs too many devices connected to bother so use my normal PC for development and then hand on to the """RPi""" when it's complete.
Steps:
#Get Arduino communicating with PC
##Upload the sketch """ASCIITable""" from Examples/communication
##While it's running, on the PC do {{{cat /dev/ttyUSB1}}} (or whatever your Arduino device is called)
##This should print out some sensible text. If so you can communicate from Arduino to PC
##If nothing comes out it may be buffering so try {{{stty -F/dev/ttyUSB1 min 1}}}
#Check PC can send to Arduino (requires an lcd display for the Arduino)
##Wire up the display. Use pins 12, 11, 5, 4, 3, 2 as these are the defaults and pins 0 and 1 are used by the serial IO.
##Upload the sketch """SerialDisplay""" from libraries/liquidcrystal/examples
##Check that what you type into the monitor comes up on the Arduino display
##If that works, try {{{echo "Hello world" >/dev/ttyUSB1}}}
##Successful two way communication!
#Develop Arduino program
#Develop PC program
#Port PC program to """RPi"""
Page about connecting """RPi""" """WiFi""": https://www.raspberrypi.org/documentation/configuration/wireless/wireless-cli.md
Page about working THROUGH the """RPi""" to get to the Arduino: http://openenergymonitor.blogspot.co.uk/2013/12/developing-for-arduino-remotely-on.html
ftpd-topfield is the easiest way to transfer files backwards and forwards between a Linux machine and the Toppy. 

The sourceforge page is no longer the most up to date (0.7.7p) for ftpd-topfield. Instead go to Toppy.org and follow the links through to http://birdman.dynalias.org/Toppy/R2-D2/

For ubuntu use the 'with statically linked libusb' version for your hardware.

To run the built binary use ftpd-new-run which does this:
{{{./ftpd -b -d -l -D -P 2021}}}
(the flags mean: turbo on, debug mode, extra syslog, daemon mode, port 2021) This will use up the terminal where you run it, but it gives you easy access to error messages etc. Other logs are in {{{tail /var/log/syslog}}}

Now you're connected to the Toppy you can use your favourite FTP tools to copy files around at {{{ftp://localhost:2021}}} in Thunar

Files in Mega sync-box
Also see: https://tutorials-raspberrypi.com/connect-control-raspberry-pi-ws2812-rgb-led-strips/
Need to run using sudo as it needs root for memory mapping. No way round this apparently.
THEREFORE do all pip3 installs using sudo
#Disable sound driver as it uses the same output as ws281x: 
##{{{modprobe -r snd_bcm2835}}} to disable it for this boot
##add a file (*.conf) to /etc/modprobe.d with "blacklist snd_bcm2835" to stop it loading next time
##comment out the audio=on line at the end of /boot/config.txt just to be on the safe side
#Dependencies:
##sudo apt install python3-numpy python3-gpio
##sudo pip3 install rpi_ws281x pyusb udmx-pyusb
#Patch up ws281x to fix errors caused by python3's range not being exactly like python2's xrange:
{{{
diff /usr/local/lib/python3.7/dist-packages/rpi_ws281x/rpi_ws281x.py fixed.py
7,10c7,10
< try:
<     xrange(0)
< except NameError:
<     xrange = range
---
> #try:
> #    xrange(0)
> #except NameError:
> #    xrange = range
36c36
<             return [ws.ws2811_led_get(self.channel, n) for n in xrange(*pos.indices(self.size))]
---
>             return [ws.ws2811_led_get(self.channel, n) for n in range(*pos.indices(self.size))]
49c49
<             for n in xrange(*pos.indices(self.size)):
---
>             for n in range(*pos.indices(self.size)):
54c54
<             return ws.ws2811_led_set(self.channel, pos, value)
---
>             return ws.ws2811_led_set(self.channel, int(pos), value)
}}}
{{{
$ convert Blues.jpg -colorspace CMYK Blues-CMYK.jpg
$ identify Blues*.jpg
Blues-CMYK.jpg JPEG 1737x2394 1737x2394+0+0 8-bit CMYK 410KB 0.000u 0:00.000
Blues.jpg JPEG 1737x2394 1737x2394+0+0 8-bit sRGB 169KB 0.030u 0:00.030
}}}
Wireless network data are stored in data/misc/wifi/wpa_supplicant.conf in plain text
Copy {{{.config/xfce4 .config/.Thunar}}} to the new installation. BUT!!

xfce caches all its settings on start up and then writes them back again at log out. It also seems to hold them across log out and log in, so on target machine:
#Log out of xfce4 session
#Copy the config files using a tty session
#Reboot the machine from the tty session before starting xfce again
Overscan is where a TV scales up the image to remove the edges of the picture. Apparently this used to be considered a good thing. When using a TV as a monitor it's a very BAD thing! Usually you can correct this on the TV by forcing it to display every pixel (usually an aspect ratio setting), however there is no 'dot for dot', 'PC', 'Exact fit', etc setting for the Asus TV monitor I have so it automatically trims the edges off the screen when using the HDMI port. A moderately random fix for this is to use the following (and put it into your .xprofile file if it works)
{{{
~$ cat .xprofile
xrandr --output HDMI-2 --set audio force-dvi --mode 1920x1080
}}}

Some other useful xrandr tricks:
{{{xrandr | grep connected}}} show all the devices known to xrandr and whether or not they are connected
{{{xrandr --prop | less}}} check out the properties available for each device
!!aegisub
The best way to create subtitles is using the excellent, open source, cross platform Aegisub, available in Ubuntu repos. This produces .ass subtitles files which can be read by VLC.
!!ffmpeg
ffmpeg is great for simple conversions. For example to create an mkv file with the subtitles in as a subtitle track at lightening speed (no transcoding needed). The {{{-disposition}}} bit says that subtitles track 0 should be displayed by default:
{{{
ffmpeg -i subtitles.ass -i input.mp4 -codec copy -disposition:s:0 default output.mkv
}}}
If you want to BURN the subtitles on top of the video use:
{{{
ffmpeg -i input.mp4 -vf subtitles=subtitles.ass output.mp4
}}}
!!VLC
Once you have ass subtitles from Aegisub you can burn them into the video using VLC:
Either command line (it's {{{soverlay}}} that does the deed)
{{{
cvlc --sout \
  "#transcode{vcodec=h264,scale=Auto,soverlay}:file{dst=output.mp4}" \
  --play-and-exit \
  --sub-file=subtitles.ass input.mp4
}}}
Or GUI:
#Make sure that VLC can play the video with the subtitles file you have
#Media/Stream (can't use Convert as it won't let you edit out the subtitle codec later on)
#Add the name of the video
#Click {{{use a subtitle file}}} and add the subtitle file name (no need if it's the same name as the source)
#Click Stream
#Click Next
#Click Add then put in destination file name
#Click Next
#click on the tools icon to the right of the profile name. 
#In the Subtitles tab choose {{{Overlay subtitles on the video}}}
#In the Audio tab click {{{keep original audio track}}}
#Click save
#Click Next
#IMPORTANT: delete the {{{scodec}}} part of the {{{Generated stream output string}}}, e.g. {{{scodec=dvbs}}}
#Click Stream
#Wait...!
!!kdenlive
A nice way to burn in subtitles which roll up at the bottom of the screen, just using kdenlive is:
#Put all the words on an image using """LibreOffice""", export as png and import the image into kdenlive as a clip (or for small amounts of text use the built-in {{{Project/Add Title clip}}} facility)
#Put the clip on the timeline and stretch to the required length
#Use the {{{Pan and Zoom}}} filter to get it in sync with the video (add keyframes as necessary)
#Use the {{{Crop, scale and position}}} filter to confine it to a line at the bottom etc
Alternatively you can put each line in as a separate Title clip. That's probably easier for synchronization but harder for getting the text in. Use aegisub if it's at all long.
!!Handbrake
Handbrake isn't very good at subtitles. You can convert the aegis ones to srt (the only format handbrake knows) using {{{ffmpeg -i subs.ass subs.srt}}} but you may well end up having to edit the result to stop handbrake putting formatting tags on the screen. Probably better to stick with VLC.

Also see: http://linux.goeszen.com/extract-subtitles-with-ffmpeg-from-a-ts-video-file.html of which goeszen says, "This post chronicles my ultimately failed attempt to extract subtitles with ffmpeg / avconv from a .ts DVB-S video-file recorded from live television."
minidlna works almost out of the box as part of a standart ubuntu install. Just need to:
**sudo adduser minidlna peter
**sudo geany /etc/minidlna.conf
{{{
media_dir=/Disks/Archive
media_dir=/Disks/Scratch2
db_dir=/Disks/Scratch/minidlna
}}}
**sudo service minidlna restart
**sudo service minidlna force-reload
**tail /var/log/minidlna.log

However mediatomb is much more capable:
#sudo apt-get install mediatomb
#sudo geany /etc/default/mediatomb and set """MT_INTERFACE""" to eth0 or whatever interface you're using
#Run the mediatomb command to start the web interface
#Navigate the Filesystem tree and press the + in a circle for each auto-update directory containing media you want served. NOTE: it's actually better (and actually quicker if you have lots of locations) to put <autoscan> options into the {{{/etc/mediatomb/config.xml}}} <import> section, especially if you have removable disks. See http://mediatomb.cc/pages/documentation#id2858022
#sudo service mediatomb restart
#tail /var/log/mediatomb
*A DVB stream file (.rec or .ts) has embedded subtitle stream(s) in either sup or sub format (graphical rather than text).
*Handbrake recognises DVB stream files but can't handle the subtitle tracks. 
*VLC can play DVB streams and recognises the subtitle track correctly.
*ffmpeg can easily produce a TS file with embedded subtitles: {{{ffmpeg -i GA.rec -scodec copy GA.ts}}}
*To extract and convert sup subtitles to sub/idx you use project-x BUT you need version >= 0.9.1 which is not in ubuntu reps so go to http://www.videohelp.com/software/ProjectX. In the {{{PreSettings/PreSettings/Subtitle}}} options of project-x you need to tick the very last option 'additional export as VobSub' - if it's not there then you have too old a version of project-x.
*SRT subtitles are in many ways neater and are the only officially supported format in mp4 (though VLC is happy with sub or sup).
*I've not found a Linux program to convert sub (graphical) to srt (text) so you need to run the Windows program {{{Subtitle-Edit}}} from http://www.videohelp.com/software/Subtitle-Edit under wine BUT you have to install dotnet version 4.0 so:
{{{
WINEPREFIX="/home/peter/.wine-SubtitleEdit" winetricks dotnet40
WINEPREFIX="/home/peter/.wine-SubtitleEdit" wine SubtitleEdit-3.4.12-Setup.exe
}}}
*The USB player in my Blaupunkt TV doesn't work with sub or sup or srt subtitles in mp4 or mkv files. 
*HOWEVER simply renaming a .rec from a Topfield DVB recording to .ts makes it work with subtitles etc in the same TV.
*project-x (v 0.91 on) allows you to cut up a rec file and preserve all the contents including subtitles (choose the {{{1:1 binary Copy}}} option in the {{{prepare>>}}} dialog.
*Another thing you can do is cut up the file with projectx, saving the video and audio in a TS file.
**Choose TS in projectx and export. This will give file[remux].ts
**Then with the same cut points demux having unselected processing of video and audio. This will give subtitles in file.sup.sub
**Now mux it all together in an mkv: {{{ffmpeg -i file-remux.ts -i file.sup.sub -vcodec copy -acodec copy file.mkv}}}

Also see [[Using avidemux to copy HD recordings from enigma2 openVIX]]
Also see: http://linux.goeszen.com/extract-subtitles-with-ffmpeg-from-a-ts-video-file.html of which goeszen says, "This post chronicles my ultimately failed attempt to extract subtitles with ffmpeg / avconv from a .ts DVB-S video-file recorded from live television."
This is the manual for my Android app "Day Clock"
https://play.google.com/store/apps/details?id=net.salisburys.dayclock&hl=en
Note that since version 4.1 the help is included in the app itself so this may be out of date.
You can find the source code at: https://github.com/peterthevicar/dementiadayclock/releases
!!What's what?
The display shows four pieces of information: the first line is for the initial text such as 'It is now'; the second line tells you the day of the week, e.g Monday; the third line tells you the period within the day, e.g. Afternoon;  the fourth line shows the actual time of day, or the date or both, e.g. 9.26 pm or 1 March (for custom formats Google for """SimpleDateFormat"""); the last line can be any text you like, e.g. an emergency phone number or important date.
!!Why 'Early hours'?
The first period is, by default, called 'Early hours'. This is to get over the problem that the day of the week changes at midnight. It can very confusing if Thursday Night changes to Friday Night at midnight as though a whole day has passed. Day Clock introduces the 'Early hours' period so it changes from Thursday Night to Friday Early hours. The 'Early hours' name also helps to emphasise that this is NOT a good time to ring relatives! Of course you can change the name of the midnight period to Night if you don't like this approach. Note that you need to have at least ONE of your periods starting at midnight or Day Clock won't have a period name to display until the period with the earliest start time begins (it doesn't crash it just leaves out the period name altogether).
!!Changing the sizes of the text. 
The first part of the settings menu (General) allows you to adjust the size of the text on each line individually. If you don't want a line to be displayed at all just set its size to 0. For example if showing the actual time is too confusing just set the 'Time text size' to 0. By default the time size is tiny as it's probably not a good idea to show it, but I wanted people to know it's available.
!!Changing the background and text colours. 
Each period during the day can have a different colour scheme (background and text colours). You set up the colour schemes in the second part of the settings menu (Colour schemes). Each colour scheme has a name (Normal, Contrast, Night etc) and each period of the day can choose one of the colour schemes for its display. By default the 'Early hours' and 'Night' periods use the Night scheme while all the others use Normal except for Tea Time which uses Contrast. The default settings for the Night theme are designed to minimise the light coming from the display.
!!Changing the names and times of the periods of the day. 
You can define up to 8 periods in a day. Each period has a name (e.g. Morning) a start and end time, a list of days it occurs and a colour scheme. To disable a period altogether just delete its name. You will normally want periods to end when the next begins (End when next period starts) giving a progression from one period to the next to the next. However you can also set the times to have a period entirely within another period, e.g. Supper time within Evening so you get Evening, Supper Time, and then back to Evening. Day Clock tries to be sensible about such overlapping periods and doesn't need the periods to be listed in the order they come in the day.
!!What's the point of the 'Period occurs every day' option? 
Most periods (e.g. Morning) are going to occur every day so you would use this option. But you may have something like 'Carers visit' which only happen on some days of the week. In that case you would clear the 'Period occurs every day' option and just select the days when the carers actually visit. Day Clock will then only display 'Carers visit' on the days you have selected (and at the time you have defined in the other settings). That would be a good example of a period which might use the Contrast colour scheme to draw attention.
!!Why would I want to save or restore my settings? 
If you've made a lot of changes to the configuration it's a good backup policy to save the settings to your device's storage. That way if you have to reset the device, or upgrade to a new device, you can copy the settings by copying the file. It's also useful if you want to try out a new configuration: save your current settings first in case it all goes wrong and you can soimply restore the saved set.
[[New Notes]]
Archive
Tried this:
{{{
iptables -I INPUT -p udp -m udp --dport 32768:61000 -j ACCEPT
}}}
But I think it was rebooting the chromecast that actually did it!
Defrag and shrink the partition as much as poss, or use dd to fill all the empty space with zeroes.

To store a compressed image of a drive:
{{{dd if=/dev/sdx1 conv=sync,noerror bs=1M | gzip -c >img.zip}}}

To put it back later:
{{{gunzip -c img.zip | dd of=/dev/sdx1 bs=1M}}}

The block size (bs) seems to be pretty much irrelevant.
Tweezers, torx driver, tiny flat head, plectrum

#Back off, battery out, SIM and SD out
#4 torx screws at corners
#2 tiny cross head screws L and R centre. Longer on R
#Lift off USB cover
#Plectrum off liner/cover/battery base
#Lift off 3 connectors on bottom R edge
#Lift USB end gently
#Re-seat camera board ribbon cable connector into underside of PCB on L
#Put everything back together
Ugly hack: put a script in {{{/lib/systemd/system-sleep}}} or better: create a systemd service file in {{{/etc/systemd/system}}}:
{{{
$ cat /etc/systemd/system/pbcs-resume.service 
[Unit]
Description=Remount external disk drives which are unmounted by suspend
After=suspend.target

[Service]
ExecStart=/usr/local/bin/post-suspend-remount.sh

[Install]
WantedBy=suspend.target
}}}
Then do {{{systemctl enable pbcs-resume.service}}}
and {{{systemctl daemon-reload}}}

This will wait for Resume to finish and then call the script in """ExecStart""" which needs to be executable. Here's mine:
{{{
$ cat /usr/local/bin/post-suspend-remount.sh 
#!/bin/bash
# remount drives after suspend
LOGF=/home/peter/post-suspend-remount.log
echo "$(date) UID=$UID" > $LOGF

sleep 5 # Give everything time to settle

systemctl daemon-reload # this is the crucial line. Without a reload the drives won't mount due to "Unit is bound to inactive unit" error

echo "-------------" >>$LOGF
mount -av 2>&1 >>$LOGF

echo "-------------" >>$LOGF
mount | grep Disks >>$LOGF
}}}

To see the journal for the suspend, e.g. errors executing your script: {{{journalctl -b -u systemd-suspend}}}
Any output to stdout from your script will show in {{{systemctl status pbcs-resume.service}}} 
or {{{journalctl -b -u pbcs-resume.service}}} 
or {{{tail /var/log/syslog}}}
Also see [[Building an Android app which is on github]]
#Read/write access: download the current state of the app from sourceforge: {{{git clone ssh://<username>@git.code.sf.net/p/androiddementiadayclock/code dayclock}}}
#Read only access: download the current state of the app from sourceforge: {{{git clone git://git.code.sf.net/p/androiddementiadayclock/code dayclock}}}
#Start up eclipse
#switch to a new workspace (File/Switch Workspace/other)
#import the code (File/Import/Android/Existing Android code into workspace/select directory where you cloned the source) don't tick "copy projects into workspace" as that duplicates the code unneccessarily.
#resore windows in eclipse (for me click restore top left then restore top right)
We have two addresses, .org and .org.uk - putting this into the .htaccess file in the rewrite section redirects everything to use .org.uk. Apparently this is better for SEO etc.
{{{
#PBCS - route everything to .org.uk
  RewriteCond %{HTTP_HOST} !=www.lymingtonchurch.org.uk [NC]
  RewriteRule ^ http://www.lymingtonchurch.org.uk%{REQUEST_URI} [L,R=301]
}}}
These are the modules in use at any time on lymingtonchurch.org. Those in parentheses are not currently being used.

#Adaptive Theme http://drupal.org/project/adaptivetheme as a base for:
#Sky subtheme http://drupal.org/project/sky which provides superb automatic adjustment for mobile devices plus easy tweaking via custom CSS; see [[Drupal 7 custom CSS tweaks to Sky Adaptive theme]]
#[[Superfish for drupal 7]] (fancy menus) which requires the external Superfish library, the superfish module and the
#Libraries module http://drupal.org/project/libraries, which is also required by
#(Views Slideshow module http://drupal.org/project/views_slideshow for a slideshow of photos (see [[How to set up Slideshow in drupal 7]]) which also needs chaos tools and)
#(Views module http://drupal.org/project/views)
#pathologic http://drupal.org/project/pathologic which allows portable use of relative path names within the site. Until it's merge, need to apply https://drupal.org/files/2071695.pathologic.relative-urls-in-subdirectory.patch for it to work in subdirectory installs
#(empty page module http://drupal.org/project/empty_page allows (front) page to be just blocks so you don't have to have a node heading) Found this isn't needed for me as the Markup Overrides for the Sky subtheme allow removal of node title (or even the whole content) for the front page.
#Pathauto http://drupal.org/project/pathauto automatically generates sensible path aliases for content (needs Token http://drupal.org/project/token)
#Globalredirect http://drupal.org/project/globalredirect redirects direct node accesses to the path alias which helps SEO.
#Nodesymlinks http://drupal.org/project/nodesymlinks allows symlinking nodes so the same node can be at two or more places in the menu
#Mobile_switch_blocks http://drupal.org/project/mobile_switch_blocks allows you to switch blocks on and off for mobile devices. I used this to change from a side menu to a top menu for mobiles. This module needs the Mobile_switch module http://drupal.org/project/mobile_switch which needs the """Mobile_Detect.php""" PHP library from http://mobiledetect.net
#Captcha and Captcha Riddler http://drupal.org/project/captcha and http://drupal.org/project/riddler  to stop automated registration of users for adding spam content
#Backup and Migrate https://drupal.org/project/backup_migrate - emails a database backup on a schedule
Tweaked CSS to date:
{{{
/* Add any valid CSS declarations */
/*** NOTE: if you use greater than (child selectors) you need to run allow-gt.php after changing ***/
/* 
 * Pallette
 * 005731 - dark green
 * 7dae3e - green
 * a3c677 - mid green
 * bed69f - pale green
 * f6faf0 - green paper
 * 7ebfdb - blue
 * a4d2e5 - mid blue
 * bfdfec - pale blue
 * e3f0f5 - v. pale blue
 * f1f8fb - blue paper
 * 8e1000 - terracotta
 * e17365 - pale terracotta
 * fff6f5 - terracotta paper * 
 */

/* Allow hovering tabs to be same as hovering menus */
ul.tabs li a:hover {
background-color: #005731;
color: #e3f0f5;
}

/* Easier to spot hyper links in nodes and breadcrumbs and tag lists */
.block-content a, 
.node-content a, 
#breadcrumb a, #breadcrumb a:visited,
h1.node-title a, h1.node-title a:visited {
  border: 2px #e3f0f5;
  padding-left: 2px;
  padding-right: 2px;
  color: #005731;
  background-color: #e3f0f5;
}
.block-content a:hover, .block-content a:focus, .block-content a:active, 
.node-content a:hover ,.node-content  a:focus, .node-content a:active,
#breadcrumb a:hover, #breadcrumb a:focus, #breadcrumb a:active,
h1.node-title a:hover, h1.node-title a:focus, h1.node-title a:active {
  background-color: #005731;
  color: #e3f0f5;
}
/* But breadcrumbs are all 'active-trail' things so terracotta */
#breadcrumb a, #breadcrumb a:visited, #breadcrumb a:hover, #breadcrumb a:focus, #breadcrumb a:active {
  color: #8e1000;
}
/* Small caps for headings */
h1, h2, h3, h4, .node-title {
  font-variant: small-caps;
}

/* Fix the header area on top of the branding on the RHS for contact us details */
.region-header {
  height: 10px; /* When the theme adapts to overflow:hidden, this will make it disappear */
  clear: none;
  position: absolute;
  left: 60%;
  top: 10px;
}

/* Make sure the logo banner is across the whole screen */
.site-logo {
  width: 100%;
}

/* Colour for recent additions block */
tr.even, tr.odd {
  background-color: #BFDFEC;
}

/* get rid of the padding around the logo */
#branding {
margin: 0 0 0px;
width: 100%;
}
header#header {
padding: 0px 0 0;
}
#logo {
padding: 0 0px;
width: 100%;
float: left;
}
#content-column, .region-sidebar-first, .region-sidebar-second {
margin-bottom: 0px;
margin-top: 0px;
}
.columns-inner {
padding-top: 0px;
}

/* Get rid of excess padding in tags lists */
figure.field-item a {
  padding-left: 0px;
  padding-right: 0px;
}
article .node-content {
  padding: 0em 0;
}
.node-content .field.field-type-image figure.clearfix.field-item {
  padding: 0px;
}
.view-mode-teaser .field-items p {
  margin: 0em;
}

/* Fix comment title / links running into previous elements */
h2.comment-title,
nav.clearfix {
  clear: left;
}

/* Menu tabs text size */
#menu-bar .menu-wrapper li a, #menu-bar .menu-wrapper .menu li a {
font-size: 1.1em;
}

/* reduce padding for edit and delete entries in user-created new content tables */
.edit, .delete {
  padding: 2px;

/* reduce size of view and edit tabs */
}
#tasks ul.primary {
height: 1.1em;
line-height: 1.1em;
}

/* reduce size of footer */
#page &gt; footer,
#page &gt; footer .block .block-inner {
padding: 0px;
}

/* Superfish Skin */
/* Override silly sky media query which inverts these on small widths */
.sf-menu.sf-style-default li li a,
.sf-menu.sf-style-default li li a:visited {
  color: #005731;
  background-color: #e3f0f5;
}
/* what we're hovering on */
.sf-menu.sf-style-default a:hover,
.sf-menu.sf-style-default li:hover,
.sf-menu.sf-style-default li a:hover,
/* Override sky media query which inverts these on small widths */
.sf-menu.sf-style-default li li:hover,
.sf-menu.sf-style-default li li a:hover,
/* hover trail stays 'lit up' */
.sf-menu.sf-style-default li.sfHover&gt;a {
  background-color: #005731;
  color: #e3f0f5;
}
/* Active trail */
.sf-menu.sf-style-default li.active-trail&gt;a,
.sf-menu.sf-style-default li li.active-trail&gt;a,
.sf-menu.sf-style-default a.sf-depth-1.active {
  color: #8e1000;
}
/* The little expansion arrows (white by default) */
.sf-menu.sf-style-default .sf-sub-indicator {
  background: url('../../superfish-arrows-005731.png') no-repeat -10px 0px;
}
.sf-menu.sf-style-default li.sfHover&gt;a&gt;span.sf-sub-indicator {
  background: url('../../superfish-arrows-e3f0f5.png') no-repeat -10px 0px;
}
/* Allow first sidebar (with superfish menu) to overflow when menus expand */
.region-sidebar-first {
  overflow: visible;
}
}}}
Source for a php command to patch up the custom.css file to allow > child selectors in the css (normally filtered out by drupal)
{{{
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
	<title>Fix up custom.css file</title>
	<meta http-equiv="content-type" content="text/html;charset=utf-8" />
	<meta name="generator" content="Geany 1.22" />
</head>

<body style="white-space: pre-wrap">
	<h3>Changed lines in custom.css file</h3>
<?php
$CCF="/Links/temp/Drupal-webserver/drupal/drupal-7.17/sites/default/files/adaptivetheme/sky_files/sky.custom.css";
$custom_css = file($CCF);
foreach ($custom_css as $line_num => $line) {
	    $new_line = str_replace("&gt;", ">", $line);
	    if ($new_line !== $custom_css[$line_num]) {
			echo $line_num . ": " . htmlspecialchars($custom_css[$line_num]) . "--&gt;" . htmlspecialchars($new_line);
			$custom_css[$line_num]=$new_line;
		}
}
file_put_contents ($CCF, $custom_css);
?>
</body>

</html>
}}}
{{{sudo aptitude install libvte9}}}
install and run cryptkeeper (puts a little key symbol in the notification area)
click on the key and follow the instructions. Recommend a two stage system:
#choose any old name but the desired location for the directory
#rename the _encfs to something more innocent looking and use import encfs to set it up using /tmp for a mountpoint
Finally set an auto-dismount timeout using left-click/Properties
Useful for working out curvatures of circles for 3D printing
If two chords intersect inside a circle then the product of the lengths of the segments of one chord equals the product of the lengths of the segments of the other chord. {{{A • B = C • D}}}

To calculate curvature:
#Measure the width {{{w}}} of the curved section from end point E to end point F
#Measure the height {{{h}}} of the curved section, i.e. the distance of the middle of the curve G from the chord EF
#The diameter from G, perpendicular to and bisecting EF, is one chord and EF the other.
#Using the equation above with {{{r}}} the required radius of curvature: {{{(w/2) . (w/2) = (2r-h) . h}}} 
#so {{{2r = h + (w^2) / 4h}}}
#{{{r = h/2 + (w^2)/8h}}}

see http://www.mathwarehouse.com/geometry/circle/product-segments-chords.php
*cpanel create new ftp user
**set strong password
**make sure directory is somewhere sensible
*cpanel file manager copy keepass kdbx file into ftp directory above
*keepass2 in xubuntu
**open url
**ftp://<server name>:21/<name>.kdbx
**username is something like <name>@<server name>
**strong password you set above
*All partition labels, sizes etc: {{{sudo lsblk -o name,mountpoint,label,size,uuid}}} or {{{sudo lsblk -o +label}}} (use {{{lsblk --help}}} to find list of -o output options). Similar information is given by the simpler {{{sudo blkid}}}
*Label a partition {{{sudo e2label -L NewLabel /dev/sda2}}} or for swap {{{sudo swaplabel -L NewLabel /dev/sda2}}}
*Find start and end LBA for all paritions {{{sudo fdisk -l}}}
*e2fsck with progress checking badblocks (-f force check; -cc non-destructive random read-write test; -C0 report progress) {{{sudo e2fsck -fccC0 /dev/sda3}}}
*List badblocks after above: {{{sudo dumpe2fs -b /dev/sda3}}}
*Process for finding which files affected by badblocks (although the e2fsck should tell you when it marks the badblocks anyway): 
**Get block list from dumpe2fs
**Find inodes affected: {{{debugfs -R "icheck BLOCK ..." DEVNAME}}}
**Find pathnames affected: {{[debugfs -R "ncheck INODE ..." DEVNAME}}}
{{{
ls /usr/share/fonts/truetype/* | grep -v "^/" | sort >fonts.sys
ls .fonts/* | grep -v "^/" | sed "s%.fonts/%%" | sort >fonts.loc
comm -12 fonts.loc fonts.sys 
}}}
Commands which will show you media info (in order of quantity of output)
file
{{{
myvideo: ISO Media, MPEG v4 system, version 1
}}}
avidemux (File/Properties)
{{{
Video
Codec 4CC: DIVX
Image Size: 720 x 576
Aspect Ratio: 1:1 (1:1)
Frame Rate: 25.000 fps
Frame Count: 7856 frames
Total Duration: 00:05:14.240

Extra Video Properties
Global Motion Compensation: No
Packed Bitstream: No
Quarter PIxel: No

Audio
Codec: MP2
Channels: Stereo
Bltrate: 16000 Bps / 128 kbps
Variable bitrate: No
Frequency: 48000 Hz
Total Duration: 00:10.28.080
File Size: 9.58 MB
}}}
mediainfo
{{{
General
Complete name                            : myvideo
Format                                   : MPEG-4
Format profile                           : Base Media
Codec ID                                 : isom
File size                                : 116 MiB
Duration                                 : 5mn 14s
Overall bit rate                         : 3 099 Kbps
Movie name                               : Funeral-compressed
Writing application                      : Lavf52.78.3

Video
ID                                       : 1
Format                                   : MPEG-4 Visual
Format profile                           : Advanced Simple@L5
Format settings, BVOP                    : Yes
Format settings, QPel                    : No
Format settings, GMC                     : No warppoints
Format settings, Matrix                  : Default (H.263)
Codec ID                                 : 20
Duration                                 : 5mn 14s
Bit rate                                 : 2 839 Kbps
Width                                    : 720 pixels
Height                                   : 576 pixels
Display aspect ratio                     : 5:4
Frame rate mode                          : Constant
Frame rate                               : 25.000 fps
Standard                                 : PAL
Color space                              : YUV
Chroma subsampling                       : 4:2:0
Bit depth                                : 8 bits
Scan type                                : Progressive
Compression mode                         : Lossy
Bits/(Pixel*Frame)                       : 0.274
Stream size                              : 106 MiB (92%)
Writing library                          : XviD 64

Audio
ID                                       : 2
Format                                   : MPEG Audio
Format version                           : Version 1
Format profile                           : Layer 2
Codec ID                                 : 6B
Duration                                 : 5mn 14s
Duration_LastFrame                       : -3ms
Bit rate mode                            : Constant
Bit rate                                 : 256 Kbps
Channel(s)                               : 2 channels
Sampling rate                            : 48.0 KHz
Compression mode                         : Lossy
Stream size                              : 9.58 MiB (8%)

}}}
exiftool
{{{
ExifTool Version Number         : 9.46
File Name                       : myvideo
Directory                       : .
File Size                       : 116 MB
File Modification Date/Time     : 2014:09:12 11:37:46+01:00
File Access Date/Time           : 2014:09:12 11:37:47+01:00
File Inode Change Date/Time     : 2014:09:12 11:37:46+01:00
File Permissions                : rw-r--r--
File Type                       : MP4
MIME Type                       : video/mp4
Major Brand                     : MP4  Base Media v1 [IS0 14496-12:2003]
Minor Version                   : 0.2.0
Compatible Brands               : isom, iso2, mp41
Movie Data Size                 : 121566239
Movie Data Offset               : 44
Movie Header Version            : 0
Create Date                     : 0000:00:00 00:00:00
Modify Date                     : 0000:00:00 00:00:00
Time Scale                      : 1000
Duration                        : 0:05:14
Preferred Rate                  : 1
Preferred Volume                : 100.00%
Preview Time                    : 0 s
Preview Duration                : 0 s
Poster Time                     : 0 s
Selection Time                  : 0 s
Selection Duration              : 0 s
Current Time                    : 0 s
Next Track ID                   : 3
Track Header Version            : 0
Track Create Date               : 0000:00:00 00:00:00
Track Modify Date               : 0000:00:00 00:00:00
Track ID                        : 1
Track Duration                  : 0:05:14
Track Layer                     : 0
Track Volume                    : 0.00%
Image Width                     : 720
Image Height                    : 576
Graphics Mode                   : srcCopy
Op Color                        : 0 0 0
Compressor ID                   : mp4v
Source Image Width              : 720
Source Image Height             : 576
X Resolution                    : 72
Y Resolution                    : 72
Bit Depth                       : 24
Video Frame Rate                : 25
Matrix Structure                : 1 0 0 0 1 0 0 0 1
Media Header Version            : 0
Media Create Date               : 0000:00:00 00:00:00
Media Modify Date               : 0000:00:00 00:00:00
Media Time Scale                : 48000
Media Duration                  : 0:05:14
Media Language Code             : und
Handler Description             : SoundHandler
Balance                         : 0
Audio Format                    : mp4a
Audio Channels                  : 2
Audio Bits Per Sample           : 16
Audio Sample Rate               : 48000
Handler Type                    : Metadata
Handler Vendor ID               : Apple
Title                           : Funeral-compressed
Encoder                         : Lavf52.78.3
Avg Bitrate                     : 3.09 Mbps
Image Size                      : 720x576
Rotation                        : 0
}}}
avprobe from the package libav-tools
avconv -i
#This finds automatically installed packages: {{{aptitude -F "%p" search \!~M~i}}}
#This looks in the dpkg log for packages installed since a particular date, but note the log is archived periodically so this only finds recent results:
{{{
#!/bin/bash
# mainly from http://ubuntuforums.org/showthread.php?t=947865
#detect all packages installed since a specified date::
if [ -z "$1" ]; then # no date given so go with the modification date of the home directory
  FDATE=$(ls --full-time -d /home | awk '{print $6 " " substr($7, 0, 9);}')
else
  FDATE=$1
fi
echo "From date $FDATE" >&2
fgrep ' status installed ' /var/log/dpkg.log \
  | sort \
  | awk "{if (substr(\$0, 1, 19) >= \"$FDATE\") {print \$5;}}" \
  | cut -d ":" -f 1 \
  | sort \
  | uniq \
  > tmp-installed-after

# Only accept what's still installed
aptitude --disable-columns -F "%p" search \!~M~i~T | sort > tmp-installed-now
comm -12 tmp-installed-now tmp-installed-after >tmp-installed-still

#detect all packages installed before the specified date (shouldn't need this but do!)
fgrep ' status installed ' /var/log/dpkg.log \
  | sort \
  | awk "{if (substr(\$0, 1, 19) < \"$FDATE\") {print \$5;}}" \
  | cut -d ":" -f 1 \
  | sort \
  | uniq \
  > tmp-installed-before

#detect all packages officially marked as auto-installed::
fgrep -B2 "Auto-Installed: 1" /var/lib/apt/extended_states \
  | fgrep "Package: " \
  | sed "s/Package: //" \
  | sort \
  > tmp-autoinstalled

#compute manually installed packages since the specified date::
comm -23 tmp-installed-still \
  <(sort tmp-installed-before tmp-autoinstalled | uniq)
rm tmp-installed-before tmp-installed-after tmp-autoinstalled \
   tmp-installed-now tmp-installed-still
}}}
This is for when the audio and video are slightly out of sync, e.g. on a zoom recording, which gives poor lip sync.
Here are two methods. The first one has the advantage of using no filters, the second one has the advantage you can watch the sync changing as you vary the parameter.
!!!Method 1, no filters:
#Put the AV clip onto the timeline and ungroup the audio from the video
#Right click on the audio track and choose "Duration"
#Set the Position a few frames up or down and then press OK
#For zoom, setting the audio to start about 11 frames (depending on frame rate) after the video seems about right and this is an easy and precise way to try different amounts of delay. 
#To advance the sound, delay the video track or crop the first few frames of the audio
#Once you have the two tracks in sync group them back together again
!!!Method 2, Tape delay simulation filter:
#Click on the 'All Audio Effects' button above the effects area
#Find the Tape Delay Simulation filter and add it to the audio track
#Use the mouse scroll wheel in the {{{Tap 1 distance (inches)}}} setting as you play the audio until it comes into sync. Note that when it's set to 0 you won't get any sound at all.
#For zoom videos you want a delay of about 0.5 seconds (i.e. 0.5 inches at one inch per second)
There is something in the graphical side of xfce4 which doesn't get initialised straight away on startup so if you have firefox or cairo-clock autostarting then they have weird graphical glitches. In the case of firefox it's black on black tooltips showing as a blank black box. The fix / workaround is to delay the autostarting for a second or two. Find the {{{Exec=}}} line in {{{.config/autostart/firefox.desktop}}} and change it from {{{Exec=firefox}}} to {{{Exec=bash -c "sleep 2; firefox"}}} this seems to delay the startup of firefox long enough for the graphical environment to be completely ready.
Stuck on Samsung SGSII logo and won't boot.
!!Set up Ubuntu PC
#install heimdall-frontend
#Download a clockworkmod image from http://unstableapps.com/builder/latest/i9100
#(No extra drivers are needed for Ubuntu)
!!Connect PC with phone
#On phone: Press (together) down, home, power to get to warning screen, then up to enter download mode
#Connect phone via USB cable to PC
#Use heimdall-frontend Utilities tab (and dmesg) to check device is connected
#It's really finicky so try different cables, ports, reboot PC, pull """SGS2""" battery etc till it works
#In Heimdall Utilities tab download the PIT file (which describes the """SGS2"""'s partitions) and save it for later
!!Upload recovery image to phone
#In Heimdall Flash tab, load up the saved PIT file
#Click on ADD in the Partitions box
#Drop down the list of partitions in the Partition Details box and choose KERNEL (''not'' RECOVERY as the recovery is built in to the kernel with the i9100)
#Find the recovery image you downloaded earlier
#Click Start
!!Reboot phone into recovery
#Make sure to hold down the Power, up and home keys to reboot into recovery otherwise it will all be overwritten
!!Flash a new ROM
#From recovery (that's why we're here, right?)
#To remove warning triangle from boot, use the Triangle Away app http://forum.xda-developers.com/galaxy-s2/orig-development/2014-01-15-triangleaway-v3-26-t1494114
*While the old system is still running, see [[Script to see what you've installed with apt]] and record what's installed
*Install from live desktop (install from boot menu failed)
*Set up network connection on correct IP

*Sort basic environment
**{{{sudo mkdir -p /Disks/OldSys; sudo mount /dev/sd?? /Disks/OldSys}}}
**{{{sudo cp -a /Disks/OldSys/Disks /Disks/OldSys/Links /}}}
**copy and amend fstab (set up disk labels using e2label) {{{sudo cp /Disks/OldSys/etc/fstab /etc; sudo mousepad /etc/fstab}}}
**{{{sudo mount -a}}}
**Laptop (T410s): {{{sudo aptitude install tlp  tp-smapi-dkms thinkfan}}} (and set up /etc/default/tlp) Note tpacpi-bat doesn't work for a T410s. Also see [[Thinkfan fan control for T410s]] May need to {{{sudo update-rc.d thinkfan enable}}}
**{{{mkdir -p /roMounts; cd /roMounts; mkdir etc Disks/Data/Peter usr/local/bin home/peter}}} mountpoints for read-only access by MEGA

*Install basic tools and get rid of some rubbish
**{{{sudo mousepad /var/lib/locales/supported.d/en}}} remove locales you don't want
**{{{sudo apt-get install aptitude}}}
**{{{sudo aptitude purge thunderbird modemmanager pidgin ristretto}}}
**See [[Remove unwanted fonts]]

*Copy Linux-system files
**{{{sudo aptitude install backintime-qt4}}}
**{{{sudo cp -a /Disks/OldSys/root/.config/backintime/ /root/.config}}}
**{{{pkexec backintime}}}
**Restore everything that's relevant from Linux-system backup
**Currently:
{{{
/etc/cron.daily/zz-pbcs-sync-site
/etc/cups/ppd
/etc/cups/printers.conf
/etc/default/grub
/etc/default/thinkfan
/etc/default/tlp
/etc/grub.d/30_os-prober
/etc/init.d/pbcs-MEGA-rofs
/etc/logrotate.d/dpkg
/etc/modprobe.d/pbcs-thinkfan.conf
/etc/sudoers
/etc/systemd/system/pbcs-resume.service
/etc/thinkfan.conf
/home/peter/.bashrc
/home/peter/.config/autokey
/home/peter/.config/autostart
/home/peter/.config/libreoffice/4/user
/home/peter/.config/Thunar
/home/peter/.config/xfce4
/home/peter/.config/gtk-3.0/bookmarks
/home/peter/.gimp-2.8
/home/peter/.kde/share/apps/digikam
/home/peter/.kde/share/config
/home/peter/.mozilla
/home/peter/.recoll/recoll.conf
/home/peter/.sitecopy
/home/peter/.sitecopyrc
/Links
/root/.config/backintime
/usr/local/bin
/usr/share/polkit-1/actions/org.freedesktop.login1.policy
/var/spool/cron/crontabs
}}}
**Note that getting xfce to see the new config is tricky, see [[Copying xfce settings to another computer]]
**Update init.d scripts {{{sudo update-rc.d pbcs-MEGA-rofs defaults}}}
**If necessary for resume to work, enable the resume re-mount service {{{systemctl enable pbcs-resume}}}

*Copy over Android stuff {{{sudo cp -a /Disks/OldSys/home/peter/Desktop/Android-Studio/ Desktop/}}}

*Install Java
{{{
sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo aptitude install  oracle-java8-installer
}}}

*Install standard extras
{{{
sudo aptitude install android-tools-adb android-tools-adbd android-tools-fastboot audacity autokey-gtk \
browser-plugin-vlc cairo-clock digikam escputil filelight flashplugin-installer fontypython \
gawk gdebi geany libvte9 gimp gimp-resynthesizer gimp-python gksudo gparted hyphen-en-gb hunspell-en-gb inkscape iotop \
lame leafpad libreoffice libreoffice-help-en-gb libreoffice-style-breeze libreoffice-style-galaxy libreoffice-style-oxygen lsscsi \
mmv mythes-en-us nethogs nfs-common nomacs p7zip-full posterazor qalculate-gtk \
recoll solaar sitecopy smartmontools speedcrunch tesseract-ocr traceroute tumbler-plugins-extra vlc winbind wine-stable xfce4-goodies xsane
}}}

*Install avidemux
{{{
sudo add-apt-repository ppa:ubuntuhandbook1/avidemux
}}}

*Set up desktop
**Copy xfce4 state from backintime ({{{.config/xfce4}}})
**Set hddtemp permissions {{{sudo chmod u+s /usr/sbin/hddtemp}}}
**See [[Correcting HDMI overscan for Asus TV monitor]]
**See [[Copying xfce settings to another computer]]
**Set up 'Preferred Applications', email should be {{{firefox "mailto:%s"}}}

*Set up recoll, which now has a separate index for each indexed drive.
**Install helpers for recoll {{{sudo aptitude install antiword pstotext pstotext python-chm python-mutagen unrtf}}}
**run {{{recoll -c /Links/<disk>/recoll}}} and set the index schedules
**run recoll locally and include those as external index entries. Alternatively copy {{{/var/spool/cron/crontabs}}} entries from the old system.

*Install Adobe Reader
**If it's a 386 version running on a 64 bit system, {{{sudo aptitude install libxml2:i386 libgtk2.0-0:i386}}}
**From ftp://ftp.adobe.com/pub/adobe/reader/unix/9.x/9.5.5/enu/AdbeRdr9.5.5-1_i386linux_enu.deb

*Install Epson driver package {{{sudo aptitude install printer-driver-escpr}}}

*Copy fontypython state
{{{
cp -a /Disks/OldSys/home/peter/.fonts /home/peter/
ln -s /Links/Fonts/0-fontypython/ /home/peter/.local/share/fontypython
}}}

*Restart cups {{{sudo service cups stop; sudo service cups start}}}
**Check cups at {{{localhost:631}}} and maybe print a test page

*Install chromium-browser if wanted and log in to sync

*Set up GB thesaurus for """LibreOffice""" en_GB:
{{{
cd /usr/share/mythes
sudo ln -s th_en_US_v2.idx th_en_GB_v2.idx
sudo ln -s th_en_US_v2.dat th_en_GB_v2.dat
}}}
*No longer needed as there is a hyphen-en-gb now
{{{
cd /usr/share/hyphen/
sudo ln -s hyph_en_US.dic hyph_en_GB.dic
}}}

*Install MEGA from https://mega.nz/#sync
**{{{cp -a /Disks/OldSys/home/peter/sync-box /home/peter; cp -a /Disks/OldSys/home/peter/.local/share/data/Mega\ Limited /home/peter/.local/share/data}}}
**Start megasync, sign in and check sync is working

*Set up the DLNA server - see[[DLNA on ubuntu - minidlna and mediatomb]]

*For 3D printing
**{{{sudo aptitude install freecad slic3r gpx}}}
**{{{ln -s /home/peter/sync-box/3D-printing/FreeCAD-config/.FreeCAD /home/peter/.FreeCAD}}}
**{{{ln -s /home/peter/sync-box/3D-printing/FreeCAD-config/.config-FreeCAD /home/peter/.config/FreeCAD}}}
**{{{ln -s /home/peter/sync-box/3D-printing/Slic3r-config /home/peter/.Slic3r}}}

*For Android Studio (also see [[Building an Android app which is on github]])
**{{{sudo aptitude install git}}}
**{{{sudo cp -a /Disks/OldSys/home/peter/Desktop/Android-Studio /home/peter/Desktop}}} Project files and Android Studio (~600MB)
**{{{sudo cp -a /Disks/OldSys/home/peter/.AndroidStudio2.1 /home/peter}}} Android Studio config (~200MB)
**{{{sudo cp -a /Disks/OldSys/home/peter/Android /home/peter}}} Android SDK (>1GB)

*For file sharing FROM this computer {{{sudo aptitude install openssh-server}}}
*For video editing: --openshot-- (too buggy) kdenlive systemsettings libav-tools project-x
*For DVD authoring: bombono-dvd
*For video transcoding and DVD ripping: handbrake (https://launchpad.net/~stebbins/+archive/ubuntu/handbrake-releases)
*For CD ripping: asunder. Set up dest {{{/Links/mp3}}}, dir name {{{%A/%L}}}, file name {{{%N)%T-%L}}}
*For Audio visualisation: projectm-pulseaudio. Use M to get menu, F for full screen, F1 for help.
*To install libdvdcss if required (medibuntu has gone) http://www.videolan.org/developers/libdvdcss.html
*Other packages: gthumb freeplane (mind maps, currently (Jun.2016) need to set JAVA_HOME to make it work) openlp (lyric and service projection) pdfshuffler (pdf stitching, extracting etc)
*For multisystem USB stick booting install via PPA as per: http://www.unixmen.com/create-multiboot-usb-ubuntu-using-multisystem/
You can make megasync run as a different user (see [[Using megasync as different user and avoid problems]]) but the problem is that it then downloads things readable only by that user. A better way is to "bind mount" another instance of your directory tree which is read only and point megasync at that instead of your read-write version. You continue to read and write as normal and megasync sees all the changes in the read only version, effectively making its sync into a backup. I did it by creating a top level directory {{{roDocuments}}} for the bind mount and then putting this script in /etc/rc2.d as S01somename:
{{{
#!/bin/bash
#present Documents as read only for backing up to MEGA
mount --bind /Disks/Data/Documents /roDocuments
mount -o remount,ro /roDocuments
}}}
I then create a sync in MEGA which points to roDocuments.
[[My primary source|http://tiddlywiki.org/#%5B%5BTiddlyWiki%20Markup%5D%5D]]

[[Character formatting markup]]
[[Links markup]]
[[Lists and indents markup]]
[[Headings markup]]
[[Tables markup]]
[[Image markup]]
''Rules:'' 
{{{----}}} (4 dashes) or {{{<hr>}}}
----
The menu item in """FreeCAD""" {{{Tools / Project Utility}}} is supposed to do this but it always crashes for me. It turns out the only special knowledge you need is that the Document.xml file must be the first added to the zip. So this works:
{{{
$ unzip project.fcstd tempdir
$ geany tempdir/Document.xml
(make changes and save)
$ zip newproject.fcstd tempdir/Document.xml tempdir/*
}}}
{{{
<style>
/* Styles for the cards */
  li.pbcs-cards, 
  li.pbcs-cards a { 
    list-style-type: none; 
    display: block;
    float: left;
    text-align: center;
    padding: 0px;
    width: 150px;
    height: 150px;
    position: relative; /* needed for z-index to work */
    z-index: 0;
    line-height: 1.2em; /* has to be here not in text span */
    border: 0;
  }
  li.pbcs-cards:hover,
  li.pbcs-cards a:hover {
    z-index: 1;
  }
/* Styles for the pictures on the cards - note they are larger than the cards to give overlap */
  img.pbcs-cards a,
  img.pbcs-cards {
    width: 150px;
    height: 150px;
    border: 0;
    padding: 0;
    background-color: #e3f0f5;
    /* Small drop shadow for all browsers */
    border-width: 1;
    border-color: #bfdfec;
    border-style: none solid solid none; /*TRBL*/
    -moz-box-shadow: 1px 1px 4px #000;
    -webkit-box-shadow: 1px 1px 4px #000;
    box-shadow: 1px 1px 4px #000;
    /* Browser specific transition duration codes */
    /* W3C */
    transition-property:width,height;
    transition-duration:1s;
    /* Firefox 4 */
    -moz-transition-property:width,height;
    -moz-transition-duration:1s;
    /* Safari and Chrome */
    -webkit-transition-property:width,height;
    -webkit-transition-duration:1s;
    /* Opera */
    -o-transition-property:width,height;
    -o-transition-duration:1s;
  }
  img.pbcs-cards:hover, 
  img.pbcs-cards a:hover {
    width: 160px; max-width: 160px;
    height: 160px;
    -moz-box-shadow: 4px 4px 16px #000;
    -webkit-box-shadow: 4px 4px 16px #000;
    box-shadow: 4px 4px 16px #000;
    /* IE8 box-shadow */
    filter: progid:DXImageTransform.Microsoft.Shadow(color=#aaaaaa,direction=135,strength=10);
  }
/* Styles for the hyperlinks which are moved down over the photos */
  .pbcs-text-up {
    position: absolute; bottom: 0px; left: 12px;
    width: 120px;
    border: 2px #e3f0f5;
    padding-top: 2px;
    padding-left: 2px;
    padding-right: 2px;
    color: #005731;
    background-color: #e3f0f5;
  }
  .pbcs-text-up:hover {
    background-color: #005731;
    color: #e3f0f5;
  }
/* Styles for rotating the cards */
  .pbcs-rotate-r {
    -webkit-transform:rotate(7deg);
    -moz-transform:rotate(7deg);
    -o-transform:rotate(7deg);
    -ms-transform:rotate(7deg);
    transform:rotate(7deg);
  }
  .pbcs-rotate-l {
    -webkit-transform:rotate(-10deg);
    -moz-transform:rotate(-10deg);
    -o-transform:rotate(-10deg);
    -ms-transform:rotate(-10deg);
    transform:rotate(-10deg);
  }
</style>
<ul>
  <li class="pbcs-cards">
    <a href="content/what-we-do"><span class=pbcs-text-up>What we do</span><img src="sites/default/files/grid/whatwedo.png" title="What we do" alt="What we do" class="pbcs-cards"></a>
  </li>
  <li class="pbcs-rotate-r pbcs-cards">
    <a href="content/church-services"><span class=pbcs-text-up>Church services</span><img src="sites/default/files/grid/churchservices.png" title="Church services" alt="Church services" class="pbcs-cards"></a>
  </li>
  <li class="pbcs-rotate-l pbcs-cards">
    <a href="content/baptisms-weddings-funerals"><span class=pbcs-text-up>Baptisms, weddings &amp; funerals</span><img src="sites/default/files/grid/baptwed.png" title="Baptisms, weddings &amp; funerals" alt="Baptisms, weddings &amp; funerals" class="pbcs-cards"></a>
  </li>
  <li class="pbcs-rotate-r pbcs-cards">
    <a href="content/about-us"><span class=pbcs-text-up>About us</span><img src="sites/default/files/grid/aboutus.png" title="About us" alt="About us" class="pbcs-cards"></a>
  </li>
  <li class="pbcs-rotate-l pbcs-cards">
    <a href="content/get-involved"><span class=pbcs-text-up>Get involved</span><img src="sites/default/files/grid/getinvolved.png" title="Get involved" alt="Get involved" class="pbcs-cards"></a>
  </li>
  <li class="pbcs-cards">
    <a href="content/bookings"><span class=pbcs-text-up>Bookings</span><img src="sites/default/files/grid/bookings.png" title="Bookings" alt="Bookings" class="pbcs-cards"></a>
  </li>
</ul>
}}}
*ssh in to the """OpenWRT""" modem/router
*Release the external IP
*wait a bit
*apply for a new external IP
{{{
$ ssh root@192.168.1.1
root@192.168.1.1's password: 
root@LEDE:~# killall -SIGUSR2 udhcpc; sleep 30; killall -SIGUSR1 udhcpc
root@LEDE:~# exit 0
Connection to 192.168.1.1 closed.
}}}

Haven't tried with shorter or absent delay than the recommended 30s.

From the manual: 
!!!signals accepted by udhcpc
udhcpc also responds to {{{SIGUSR1}}} and {{{SIGUSR2}}}. 
{{{SIGUSR1}}} will force a renew state, and 
{{{SIGUSR2}}} will force a release of the current lease, and cause udhcpc to go into an inactive state (until it is killed, or receives a {{{SIGUSR1}}}). 
You do not need to sleep between sending signals, as signals received are processed sequencially in the order they are received.
!!!The expectation
I've paid for Amazon Prime, I get free video streaming and Amazon say it works on Android devices so I'll stream some on my MBOX Android TV box. It will be easy!

!!!The hardware
eBay from China, described as {{{DVB T2 CS818II Dual Core Android 4.0 TV Box 1GB/8GB Bluetooth 4.0 HDMI RJ45 HD}}}. Google reports a sign in from {{{Acer Iconia}}} and the Android device manager says it's a {{{8726_MX-T2}}}. Running Android 4.2.2. """DVB-T2""" (HD Freeview) tuner and rubbish app for providing Freeview (e.g. "what's on" guide doesn't work), has Play Store, gmail etc pre-installed. """2 x USB ports, SD slot, HDMI 1.4a, Digital audio co-ax out, AV out, RJ45 ethernet socket, Aerial co-ax input, WiFi aerial. AMLogic8726-MX 1.5GHz, Cortex A9 , Dual Core, DDR3 : 1GB, Nand Flash:8G.""" The marking on the case says {{{DVB-T2 Terrestrial CS818II}}}.

!!!The problem
If you follow the instructions and install Amazon Appstore (sideloading from the amazon website), then install Amazon Video from the Amazon Appstore, you get as far as playing a video from its summary page, the player goes away for a bit with the wait animation and then goes grey, fails and returns to the video's summary page. Why does it take so much effort to connect to a streaming service I've paid for (Amazon Prime)????

!!!Background
There are three apps involved: 
#Amazon (also called Amazon shopping, Amazon for Tablets, Amazon underground)
#Amazon Appstore, where you find:
#Amazon Video (which actually plays the video)
There are several versions of the first app, don't use the one from the Google Playstore, use the one from the Amazon Appstore, called Amazon Underground. There are also several versions of the third and most important app and the only one that seems to work on my MBOX is the second of the two downloads here: http://forum.xda-developers.com/shield-tv/development/amazon-prime-video-demand-android-tv-t3278778 
It invites you to try another method first which DOESN'T work on my MBOX.

!!!Solution
#Uninstall any Amazon apps you have; factory reset if it's not too much hassle
#Install the Amazon Appstore from Amazon's web site by downloading and sideloading it
#Download and install the VOD apk ({{{com.amazon.avod.thirdpartyclient.apk}}}) from the xda forum above (it installs as Amazon Instant Video)
#Download the Amazon Underground (AU) app from the Amazon Appstore - I'm not sure this is totally necessary but it's a handy app anyhow
#Either from within AU or by launching Amazon Instant Video directly, start a video.
#Rejoice if it works!
#Downside: it doesn't allow downloading to the device whereas the official app does. Just a shame the official one can't actually play the video!

!!!Misc
To reset the MBOX if it gets stuck in a boot loop etc: 
#Pull mains lead
#Put a paperclip into the RST hole and hold down the reset button
#While still holding the reset button plug in the mains again
#Wait for a long time till you get the little android robot with a menu
#On the device's original remote (didn't work with a wireless USB remote for me) use the up/down cursor keys and OK to navigate the menu and try clear cache first or factory reset if that fails too.
Turns out you can't join either Calc spreadsheets or """DBase""" dbf files in Base. If your data is in that format:
#link to them in a Base file A
#create a new empty Base file B
#copy and past the tables from A to B, accepting a primary key called ID
#use the design view to create a new query in B and you should find you can add multiple tables
#you can copy and past queries from A to B as well
If HD video is not smooth, try Tools/ Preferences/ Input/Codecs/ skip H.264/ Non-key
The main slider is the RF slider, 0 is lossless, 20 is normal. Tests done on 2.7Gig quad core i5, 6 minutes of a monkey programme - lots of movement and detail.
Setting is for High Profile, just change the RF
|RF|fps encode|MB/min out|GB/hr|Subjective quality (32" monitor)|h
|35|46|5.4|0.3|Watchable but considerable loss of detail full screen|
|30|40|8.4|0.5|OK full screen, noticeable blockiness in the detail|
|25|31|16.5|1|Very slight loss of quality e.g. blurring in hair, barely noticeable|
|25*|50|16|1|Seems as good as with the Preset at medium|
|20|26|35.8|2|As good (and big) as the original|
*Moved "Preset" from medium to faster (2 positions left)
On balance I think I'll go with RF 25 and Preset faster
| Format | Result | Markup |h
|Heading level 1|<html><h1>text</h1></html>|{{{!text}}}|
|Heading level 2|<html><h2>text</h2></html>|{{{!!text}}}|
|Heading level 3|<html><h3>text</h3></html>|{{{!!!text}}}|
|Heading level 4|<html><h4>text</h4></html>|{{{!!!!text}}}|
|Heading level 5|<html><h5>text</h5></html>|{{{!!!!!text}}}|
Not many changes to the markup language, mainly:
*In TWC line ends are line ends. In TW5 you need TWO returns to start a new paragraph, single line ends are ignored unless you enclose a block with lines of (only) three double quotes {{{"""}}}
*Lists need blank lines before them
*"""{{{text}}}""" becomes """`text`"""
*"""{{{""" for code blocks becomes """```"""
*{{{"""}}}Nowiki{{{"""}}} becomes """~Nowiki""" but it only works for camel text so ~norm renders as ~norm but """~NoRm""" renders as """NoRm"""
*"""--strikethrough--""" becomes """~~strikethrough~~"""
*"""sub~~script~~""" becomes """sub,,script
There is a file called {{{/etc/apt/apt.conf.d/10periodic}}} which controls the {{{/etc/cron.daily/apt}}} script (see the header in that script for details) 
To make it clear up all the deb files in {{{/var/cache/apt/archive}}} every day set {{{APT::Periodic::AutocleanInterval "1";}}}
Open a hole in the firewall:
{{{sudo iptables -I INPUT -p udp -m udp --dport 32768:60999 -j ACCEPT}}}
Following a failed resumne you can get left with system crash report pop-ups. With no apport they will be there for ever. To stop the system trying to report errors you need to delete the crash files:
{{{
sudo rm /var/crash/*.crash 
}}}
Putting drupal in a subdir is a good idea. The trick is setting up the root {{{.htaccess}}} file correctly. You also need to change the file {{{sites/default/settings.php}}} to set {{{$base_url}}} to be just the top level of the website (otherwise the URL is reported in the browser complete with the subdir).
{{{
#PBCS - route everything to .org.uk
  RewriteCond %{HTTP_HOST} !=www.lymingtonchurch.org.uk [NC]
  RewriteRule ^ http://www.lymingtonchurch.org.uk%{REQUEST_URI} [L,R=301]

#PBCS Serve Drupal from sub directory in web root - this seems to work, try next time!
  RewriteRule ^$ drupal-7.28/index.php [L]
  RewriteCond %{DOCUMENT_ROOT}/drupal-7.28%{REQUEST_URI} -f
  RewriteRule .* drupal-7.28/$0 [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule .* drupal-7.28/index.php?q=$0 [QSA]

#PBCS - these are the previous rewrite lines
  # Pass all requests not referring directly to files in the filesystem to
  # index.php. Clean URLs are handled in drupal_environment_initialize().
#  RewriteCond %{REQUEST_FILENAME} !-f
#  RewriteCond %{REQUEST_FILENAME} !-d
#  RewriteCond %{REQUEST_URI} !=/favicon.ico
#  RewriteRule ^ index.php [L]
}}}
*Put a file in /usr/share/cups/banners like this (see http://localhost:631/help/spec-banner.html?QUERY=banner for options):
{{{
/usr/share/cups/banners$ cat pbcs-ink
#PDF-BANNER
Template pbcs-ink.pdf
Show printer-name time-at-processing
}}}
*Put a customised pdf into {{{/usr/share/cups/data}}} with the name referenced in the file above
*{{{sudo /etc/init.d/cups restart}}}


#Download and install http://drupal.org/project/views_slideshow and http://drupal.org/project/ctools
#Download jquery.cycle.all.latest.min.js from https://github.com/malsup/cycle/downloads and put it into sites/all/librariesjquery.cycle named jquery.cycle.all.min.js
#(also need the libraries module which is also needed for superfish menus)
#Create a new content type (e.g. slide) for your slides with an image type field.
#Create some content of that type (slide) which will show in the slideshow
#Create a new View (Structure / Views) as a block which picks up that type of content and has Format set to Slideshow (Configure the transitions etc by clicking Settings next to Format: Slideshow), call the view something like Slideshow-view
#Control whether title is shown in Structure / Views / edit. Under the heading FIELDS find Content: <fieldname>, click on the field name and check the exclude from display box.
#Add the block to whatever part of the display you like (Structure / Blocks)
#install vino
#run vino-preferences
#start the server {{{/usr/lib/vino-server}}}
#On Android device install wyse Pocket Cloud https://play.google.com/store/apps/details?id=com.wyse.pocketcloudfree
#Unpack a fresh copy of drupal
#Unpack fresh copies of the modules, themes, libraries etc
#Pack up the resulting directory structure and upload to the destination server and unpack it there
#Export the database structure and contents (not cache contents)
#Change the table prefix in the exported sql files as required
#Import to the destination database
#Copy the icon and banner files from sites/default/files (and any images etc you want)
#Make sure permissions are set correctly
#Copy the sites/default/settings.php and correct the database prefix, password etc
#Go into theme appearance settings and save
#Go to allow-gt.php if required
The problem is that {{{winetricks ie6}}} only works on 32 bit wine so you have to
#Create a new 32 bit wine disk: {{{WINEARCH=win32 WINEPREFIX=$HOME/.wine32 winecfg}}}
#Run winetricks using the new disk: {{{WINEARCH=win32 WINEPREFIX=$HOME/.wine32 winetricks ie6}}}

winetricks will ask you to download msie60.exe and show you where to get it.
Simple Images: {{{[img[http://www...jpg]]}}}
Tooltips for Images: {{{[img[tooltip|http://wikitext.tiddlyspace.com/fractalveg.jpg]]}}}
Displays as:
[img[tooltip|http://wikitext.tiddlyspace.com/fractalveg.jpg]]
Image Links: {{{[img[http://www...jpg][http://www...html]]}}}

Floating Images with Text:
*Left:{{{[<img[http://www...jpg]]}}}
*Right: {{{[>img[http://www...jpg]]}}}
*Clear float: {{{@@clear:both;display:block; @@}}}
Also see [[Doing something after a resume from suspend]]
!!!Documents
Overview page: https://www.freedesktop.org/wiki/Software/systemd/
Manual: https://www.freedesktop.org/software/systemd/man/
Manual for units: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
FAQ: https://www.freedesktop.org/wiki/Software/systemd/FrequentlyAskedQuestions/
Network issues: https://www.freedesktop.org/wiki/Software/systemd/NetworkTarget/
Tips and tricks: https://www.freedesktop.org/wiki/Software/systemd/TipsAndTricks/
!!!Editing
After you've edited dependencies of units (Before=, After= etc) you need to rebuild the tree via {{{systemctl daemon-reload}}}
After you've edited the content of a unit ({{{ExecStart=}}} etc) you need to restart that unit via {{{systemctl restart <unitname>}}}
!!!Where things are
systemctl units are stored in {{{/lib/systemd/system/}}} with suffix to indicate the type of unit, e.g. {{{.service}}} or {{{.target}}}
Overrides are put in {{{/etc/systemd/system}}} and take precedence over the versions in {{{/lib}}}
You can put your own bits into {{{<unitname>.wants}}} directories. Normally these would be symlinks to other units but can be a new unit.
All fstab mounts have auto-produced units such as {{{Disks-Scratch.mount}}}
!!!Common operations
You can see what's happening to a unit: {{{systemctl status Disks-Scratch.mount}}}
Also see what dependencies it has: {{{systemctl list-dependencies --after network-online.target}}}
or what depends on it: {{{systemctl list-dependencies --before network-online.target}}}
#Fourier
##Download Fourier plugin from http://registry.gimp.org/node/19596
##Unpack - the binary is 64 bit, to build a 32 bit version:
###sudo aptitude install libfftw3-dev libgimp2.0-dev
###rm fourier
###make
##{{{make install}}} (copies binary to plugins directory)
##Start Gimp and check Filters/Generic for FF filters
#Descreen
##Download scm script file from http://registry.gimp.org/node/24411 to .gimp-2.8/scripts folder
##Make changes/fixes as suggested at http://www.zoyinc.com/?p=1529 but change the back quote to a single quote when setting the mid-grey colour
##Start Gimp and check Filters/Enhance/Descreen
#Download firmware from https://downloads.openwrt.org/ choose a version then ramips/rt305x and look for the correct hardware revision (d2 in my case) and get the factory.bin
#Put the router in emergency mode by pulling the plug, holding down the reset button with a paper clip, plug in again and wait for the power light to flash orange.
#Set your computer network to be wired with 192.168.0.2 IP
#Connect your computer to a LAN port on the router
#Using firefox (apparently chrome doesn't work) go to 192.168.0.1
#Should get the recovery dialog allowing you to upload the openwrt firmware
#Connect your computer to the router using DHCP
#Connect to openwrt at 192.168.1.1 (only worked after a reboot for me)
#https://wiki.openwrt.org/doc/howto/firstlogin
Sometimes you get unhelpful failures like:
{{{
Connecting to download.oracle.com (download.oracle.com)|78.151.231.104|:80... connected.
HTTP request sent, awaiting response... 416 Requested Range Not Satisfiable

    The file is already fully retrieved; nothing to do.

Download done.
Removing outdated cached downloads...
sha256sum mismatch jdk-8u25-linux-i586.tar.gz
Oracle JDK 8 is NOT installed.
}}}

To get it installed you need to remove the cached partial download and then try again. The quickest way to do that is:
{{{
sudo rm /var/cache/oracle-jdk8-installer/jdk-8*.tar.gz
sudo /var/lib/dpkg/info/oracle-java8-installer.postinst configure 
}}}
Excellent web page at: https://help.ubuntu.com/community/Custom%20keyboard%20layout%20definitions
How to show a keyboard layout graphically (the example shows the intl sub-layout of the us layout which has to be separated by a tab character)
{{{gkbd-keyboard-display -l $'us\tintl'}}}
If you use hyphens (minus signs) in names of tables or databases in """LibreOffice""" it stops mail merge working properly and you get the same label over and over again, as though you didn't have a <Next record> field.
| Format | Result | Markup |h
|"""WikiWord"""|WikiWord|{{{WikiWord}}}|
|Explicit|[[WikiWord]]|{{{[[WikiWord]]}}}|
|Pretty|[[text|WikiWord]]|{{{[[text|WikiWord]]}}}|
|External|http://www.tiddlywiki.com|{{{http://www.tiddlywiki.com}}}|
|~|[[text|http://www.tiddlywiki.com]]|{{{[[text|http://www.tiddlywiki.com]]}}}|
|Local|file:///folder/file|{{{file:///folder/file}}}|
|~|[[text|file:///folder/file]]|{{{[[text|file:///folder/file]]}}}|
| Format | Result | Markup |h
|CSS Indent*|text<br>{{indent{text2}}}|<html><code>{{indent{text2}}}</code></html>|
|Numbered list**|<html><ol><li>text</li><ol><li>text2</li><ol><li>text3</li></ol></ol></ol></html>|{{{#text}}}<br>{{{##text2}}}<br>{{{###text3}}}|
|Bullet list|<html><ul><li>text</li><ul><li>text2</li><ul><li>text3</li></ul></ul></ul></html>|{{{*text}}}<br>{{{**text2}}}<br>{{{***text3}}}|
|Blockquote|<html><blockquote>text<blockquote>text2<blockquote>text3</blockquote></blockquote></blockquote></html>|{{{>text}}}<br>{{{>>text2}}}<br>{{{>>>text3}}}|
|Blockquote block|<html><blockquote>text<br>text2<br>text3</blockquote></html>|{{{<<<}}}<br>{{{text}}}<br>{{{text2}}}<br>{{{text3}}}<br>{{{<<<}}}|
|Monospace block|<html><code>text<br>text2<br>text3</code></html>|<html><code>{{{</code></html><br>{{{text1}}}<br>{{{text2}}}<br>{{{text3}}}<br><html><code>}}}</code></html>|
"""*"""Also consider other uses of CSS (see [[Character formatting markup]]).
"""**"""Indents within lists:
{{{
 #a: text
 #b: text {{indent{
 text3
 text4
 }}}
 #c text
}}}
#a: text
#b: text {{indent{
text3
text4
}}}
#c text
/***
|''Name:''|LoadRemoteFileThroughProxy (previous LoadRemoteFileHijack)|
|''Description:''|When the TiddlyWiki file is located on the web (view over http) the content of [[SiteProxy]] tiddler is added in front of the file url. If [[SiteProxy]] does not exist "/proxy/" is added. |
|''Version:''|1.1.0|
|''Date:''|mar 17, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#LoadRemoteFileHijack|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
***/
//{{{
version.extensions.LoadRemoteFileThroughProxy = {
 major: 1, minor: 1, revision: 0, 
 date: new Date("mar 17, 2007"), 
 source: "http://tiddlywiki.bidix.info/#LoadRemoteFileThroughProxy"};

if (!window.bidix) window.bidix = {}; // bidix namespace
if (!bidix.core) bidix.core = {};

bidix.core.loadRemoteFile = loadRemoteFile;
loadRemoteFile = function(url,callback,params)
{
 if ((document.location.toString().substr(0,4) == "http") && (url.substr(0,4) == "http")){ 
 url = store.getTiddlerText("SiteProxy", "/proxy/") + url;
 }
 return bidix.core.loadRemoteFile(url,callback,params);
}
//}}}
Once you've tried to pair the mouse with bluetooth, it is no longer paired with the unifying receiver. To re-pair you need either Logitech's software on Windows or Solaar on Linux.
*My setup: laptop (Thinkpad) in docking station connected via a Display Port adapter to an HDMI screen.
*The problem: When you suspend the system the monitor times out (no signal) which is what you want. BUT when you resume xfce thinks there's no HDMI monitor any more (because it's turned off) so it falls back to the laptop screen, the laptop lid is closed so that makes the Power Manager shut down or suspend the system. Darn!
*The workaround: always remember to turn on the HDMI monitor before resuming the system. If you forget you can blindly talk to xfce by pressing {{{[Super-P]}}} to re-enable the HDMI output (try it while the HDMI screen's up and running to see how it works).
*The other problem: If you're running something that takes a while, e.g. a video transcode, xfce times out the screen as requested in Power Manager, the monitor switches off (no signal), or you switch off the monitor manually to save power, and down goes the system killing the transcode. Darn!
*The workaround: click on the battery icon in the xfce panel and choose {{{Presentation mode}}} and DON'T switch off the monitor. Yuk!
*''The solution:'' put a simple desktop file into your {{{.config/autostart}}} folder. It re-enables the {{{HDMI2}}} (adjust as required) output every 10 seconds. Sorted! The down-side of this very simple approach is that if you use different configurations for {{{HDMI2}}} for some reason, it will switch it back to the auto configuration. It would be easy enough to write a script to take that into account by querying xrandr before calling it to reconfigure. For 99% of cases this is all you need:
{{{
~$ cat .config/autostart/hdmi2-polling.desktop
[Desktop Entry]
Name=PollHDMI2
Comment=every 10 seconds reconfigures HDMI2 in case it's been turned off
Exec=sh -c "while true; do xrandr --output HDMI2 --auto; sleep 10; done"
Icon=video-display
Terminal=false
Type=Application
Categories=AudioVideo;
}}}
Use kid3, easy and brilliant
[[New Notes on TiddlyHost|https://peterthevicar.tiddlyhost.com]]
[[Formatting notes]]
[[Archive|http://peterthevicar-archive.tiddlyspot.com]]
Symptom: when reading a DVD it gets faster and faster and then fails, usually due to being unbalanced and wobbling too much. To limit the maximum speed use eject. {{{eject -X}}} shows what speeds are available, {{{eject -x4}}} limits speed to 4x etc.
Useful notes at https://help.ubuntu.com/community/AndroidSDK
#Ensure icedtea and its dependency openjdk jre are installed
#Download ADT bundle from http://developer.android.com/sdk/index.html and unpack somewhere
#Start the SDK manager by running <bundle>/sdk/tools/android
#Install updates as reported by the SDK manager
#Click on the latest API to select components (e.g documentation) and install
#Start the eclipse SDK <bundle>/eclipse/eclipse
#Set the location of the sdk (navigate to <bundle>/sdk)
#http://developer.android.com/training/basics/firstapp/creating-project.html
#If you just want a slideshow on a laptop, most image viewing programs have the feature, including nomacs which has fade, swipe and appear transitions.
#For a pan and zoom transition which is rendered to a video file, Kdenlive allows you to do this easily:
##Don't forget to set the project to the output settings you need before importing the slideshow clip or you'll have to do it again.
##Add a directory full of photos using Project / Add Slideshow Clip. 
##There is a Transition drop-down which allows quite interesting transitions. 
##This is less flexible than adding all the photos individually and managing things yourself but saves a huge amount of time. 

Use aegisub to add subtitles separately if required (see [[Creating subtitles in video]]).

To generate a template for the image subtitles you can use a python script like this, adjust the range to the number of images in your slideshow:
{{{
#!python3
for i in range(100):
	startTime = i*5 + 1
	endTime = startTime + 3
	startMins = startTime // 60
	endMins = endTime // 60
	startSecs = startTime % 60
	endSecs = endTime % 60
	print ('Dialogue: 0,0:{:02d}:{:02d}.00,0:{:02d}:{:02d}.00,Large,,0,0,0,,Image{:03d}'.format(startMins, startSecs, endMins, endSecs, i+1))
}}}
#First generate a subtitle that looks good to you in aegis and save the ass file
#Then run the python script
#Append the output into aegisub's generated ass file as a starting point
With xubuntu 13.04 the default 'final' printer driver is the pdfprinter which gets the margins wrong. The good news is that the alignmargins script actually works for this setup. Download from http://www.linuxfoundation.org/collaborate/workgroups/openprinting/databasecupsdocumentation

The values which worked for my printer were:
{{{
ml 0
mb 0
mr 0
mt 0
x -14
y 10
}}}
There are three that work within Linux:
#YUMI - http://www.pendrivelinux.com/yumi-multiboot-usb-creator/
#"""MultiSystem""" - http://sourceforge.net/projects/multisystem/
#Multibootusb - http://sourceforge.net/projects/multibootusb/

When you boot a YUMI stick you get a {{{boot:}}} prompt, just hit TAB for options.

Multi System seems to be the best to me. If you get errors booting with any of these if can be that your BIOS doesn't work with fast or large USB media. It gives errors along the lines of missing media, file not found etc.
Mainly from: https://lede-project.org/docs/user-guide/nfs_configuration

*ssh from connected PC (e.g. ssh root@192.168.1.4) and create the directories you want to share (note this will need to be re-done after each update)
*LEDE: 
**Install nfs support in System/Software: block-mount, kmod-usb-storage, nfs-kernel-server
**Set up all your mounts (I use /nas) and check they work using {{{ls}}}
*ssh from connected PC and 
**edit /etc/exports to say:
{{{
root@LEDE:~# cat /etc/exports
/nas/Media 192.168.1.0/24(rw,sync)
/nas/Scratch 192.168.1.0/24(rw,sync)
/nas/Archive 192.168.1.0/24(rw,sync)
root@LEDE:~# 
}}}
**Use {{{service nfsd reload}}} to start sharing the directories

The server is now set up. To set up the clients to be able to see the nas mounts:
*Thunar won't find them; it doesn't know about nas
*So you have to use /etc/fstab, e.g.:
{{{
192.168.1.4:/nas/Archive /Disks/Archive	nfs		rw,_netdev,nofail	0		2
192.168.1.4:/nas/Scratch /Disks/Scratch	nfs		rw,_netdev,nofail	0		2
192.168.1.4:/nas/Media	/Disks/Media	nfs		rw,_netdev,nofail	0		2
}}}
*{{{sudo mount /Disks/Media}}} etc
[[New Notes on TiddlyHost|https://peterthevicar.tiddlyhost.com]]
Xubuntu 18.04 installs numlockx which is controlled by /etc/default/numlockx
It is set to {{{auto}}} by default which turns """NumLock""" on by default except for laptops. Change to {{{off}}} if your keyboard is a mini one with no number pad
Firefox annoyingly insists on using dolphin to open folders if you've installed dolphin. This overrides every other setting for default actions because it registers the dbus service """FileManager1""":
{{{
~$ cat /usr/share/dbus-1/services/org.kde.dolphin.FileManager1.service
[D-BUS Service]
Name=org.freedesktop.FileManager1
Exec=/usr/bin/dolphin --daemon
}}}

To cancel this out you need to put an overriding dbus service under the same name in your .local directory structure:
{{{
~$ cat .local/share/dbus-1/services/org.kde.dolphin.FileManager1.service 
[D-BUS Service]
Name=org.freedesktop.FileManager1
Exec=/bin/false
}}}
This effectively makes the router into a switch, relaying data in both directions between the """WiFi""" receiver and transmitter, keeping everything on the same subnet. The slight issue is that the control interface has to be on a different subnet (technical restriction) so you need to set up a network connection with a manual IP address to access """LuCI""" once it's done.

see https://openwrt.org/docs/guide-user/network/wifi/relay_configuration

also see [[openwrt WISP, repeater, routed client, hotel repeater, Client+AP]] which joins clients on a different subnet from the repeated network and doesn't need relayd.

Basically:
#change the IP of the LAN interface to a different subnet, e.g. 192.168.2.1
#reboot and reconnect using a manual IP
#change the hostname to something sensible
#Network/Wireless and join the network to allow download of software
##client will be on wwan interface
##wwan needs to be in LAN firewall group
#System/Software update package list
#Install relayd and luci-proto-relay ("""LuCi""" control for relayd)
#Set up wireless interface Master (AP) on the LAN interface, use whatever SSID and security you like. This will be transmitting the repeated data
#set up relayd:
##Go to network interfaces and add a new interface with a suggestive name, e.g. relayd
##give it an IP address on the shared subnet
##set relayd to be a relay and to relay between LAN and wwan networks
##put relayd in LAN firewall zone
#reconnect using DHCP, should get an IP on the shared subnet, check access to router
#(MAY need to reboot)
#(MAY need to disable DHCP for the LAN interface if it stops relayd working)
#You will need to connect as 192.168.2.1 to get to the """LuCi""" interface.

NOTE: if you lose all the settings on reboot this is because the """OpenWRT""" image is too large for the router's memory ("""DIR-615""" has only 4MB). In this case need to download the buildimage archive then build excluding some packages:
{{{
CONFIG_IPV6=n make image PROFILE=dir-615-d PACKAGES="uhttpd uhttpd-mod-ubus libiwinfo-lua luci-base luci-app-firewall luci-mod-admin-full luci-theme-bootstrap -ppp -ppp-mod-pppoe -ip6tables -odhcp6c -kmod-ipv6 -kmod-ip6tables -odhcpd-ipv6only"
}}}
Copy the program below and paste into a new file on the router (over ssh), e.g. user-installed.sh, then run it. The program uses awk to find the earliest time any package has been installed (which it calls """FLASH_TIME""") and then prints a list of all packages with a more recent installation time. The time is in the format of {{{date +%s}}}, i.e. seconds since 1970.
{{{
echo "System time now: $(date +%s) ($(date))"

FLASH_TIME="$(awk '
$1 == "Installed-Time:" && ($2 < OLDEST || OLDEST=="") {
  OLDEST=$2
}
END {
  print OLDEST
}
' /usr/lib/opkg/status)"

echo "Searching for packages installed more recently than $FLASH_TIME ($(date $FLASH_TIME))"

awk -v FT="$FLASH_TIME" '
$1 == "Package:" {
  PKG=$2
  USR=""
}
$1 == "Status:" && $3 ~ "user" {
  USR=1
}
$1 == "Installed-Time:" && USR && $2 > (FT+10) {
  print PKG, $2
}
' /usr/lib/opkg/status | sort
}}}
/***
|''Name:''|PasswordOptionPlugin|
|''Description:''|Extends TiddlyWiki options with non encrypted password option.|
|''Version:''|1.0.2|
|''Date:''|Apr 19, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#PasswordOptionPlugin|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (Beta 5)|
***/
//{{{
version.extensions.PasswordOptionPlugin = {
	major: 1, minor: 0, revision: 2, 
	date: new Date("Apr 19, 2007"),
	source: 'http://tiddlywiki.bidix.info/#PasswordOptionPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	license: '[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D]]',
	coreVersion: '2.2.0 (Beta 5)'
};

config.macros.option.passwordCheckboxLabel = "Save this password on this computer";
config.macros.option.passwordInputType = "password"; // password | text
setStylesheet(".pasOptionInput {width: 11em;}\n","passwordInputTypeStyle");

merge(config.macros.option.types, {
	'pas': {
		elementType: "input",
		valueField: "value",
		eventName: "onkeyup",
		className: "pasOptionInput",
		typeValue: config.macros.option.passwordInputType,
		create: function(place,type,opt,className,desc) {
			// password field
			config.macros.option.genericCreate(place,'pas',opt,className,desc);
			// checkbox linked with this password "save this password on this computer"
			config.macros.option.genericCreate(place,'chk','chk'+opt,className,desc);			
			// text savePasswordCheckboxLabel
			place.appendChild(document.createTextNode(config.macros.option.passwordCheckboxLabel));
		},
		onChange: config.macros.option.genericOnChange
	}
});

merge(config.optionHandlers['chk'], {
	get: function(name) {
		// is there an option linked with this chk ?
		var opt = name.substr(3);
		if (config.options[opt]) 
			saveOptionCookie(opt);
		return config.options[name] ? "true" : "false";
	}
});

merge(config.optionHandlers, {
	'pas': {
 		get: function(name) {
			if (config.options["chk"+name]) {
				return encodeCookie(config.options[name].toString());
			} else {
				return "";
			}
		},
		set: function(name,value) {config.options[name] = decodeCookie(value);}
	}
});

// need to reload options to load passwordOptions
loadOptionsCookie();

/*
if (!config.options['pasPassword'])
	config.options['pasPassword'] = '';

merge(config.optionsDesc,{
		pasPassword: "Test password"
	});
*/
//}}}
If you have a newish ubuntu setup then the power management is via {{{systemd}}} which has a useful tool called {{{systemd-inhibit}}}. This can inhibit various actions while a command is running. So I have a media server which I run using this script:
{{{
service mediatomb start

systemd-inhibit \
  --what=shutdown:sleep:handle-suspend-key:handle-power-key:handle-lid-switch \
  --who="mediatomb server" \
  --why="Prevent shutdown of media server" \
  --mode="block" \
  zenity --warning --title mediatomb server running --text "mediatomb server is preventing shutdown and suspend.\n\nClick OK to close the media server"

service mediatomb stop
}}}
The inhibit is in place as long as the zenity dialog is showing and when you dismiss the warning dialog the server is shut down. You might think you could make it simpler and just have the inhibit for the {{{service start}}} call but of course that only lasts as long as it takes to start the server so doesn't do what you want.
Symptom: ubuntu 13.10 suspend won't wake on r200, just get non-responsive black screen

Data: worked with ubuntu 12.10

Solution: binary chop on kernel version from http://kernel.ubuntu.com/~kernel-ppa/mainline (need image i386, image extra i386, headers i386 and headers all)
Which versions work?
#3.2 (12.04 precise) YES
#3.6.3 (12.10 quantal) YES
#3.9.0 (13.04 raring) YES
#3.9.0 (13.10 saucy, 7.May.15) YES
#3.9.5 (saucy, 7.Jun.13) YES
#3.9.9 (saucy, 3.Jul.13) YES
#3.10-rc1 (saucy, 13.May.13) NO
#3.10.0 (saucy) NO
#3.11 (saucy) NO

!!!!Useful pm-suspend debugging stuff
https://wiki.debian.org/Suspend looks useful although it says hal is involved whereas I don't think it is any more
Also: something odd's going on because there are definitely TWO pm systems having a go, e.g. suspend wakes then suspends again, second time wakes with a screensaver.

{{{pm-suspend}}} can take {{{--quirk-*}}} parameters, it looks like you have also to use {{{--quirk-test}}} or it ignores quirks for kms video drivers
Setting up a virtual python environment is a good idea because you know exactly what you've installed for this particular program to work.
#Install virtualenv (this installs python3-virtualenv)
#Install python3-pip
#Download from python.org the version of python you want to use
#Extract the files somewhere
#open a terminal in the python-<version> subdirectory
#Install libffi-dev to fix error {{{ModuleNotFoundError: No module named '_ctypes'}}}
#Install libssl-dev to fix error {{{Can't connect to HTTPS URL because the SSL module is not available}}}
#Install libatlas-base-dev to fix error (RPi only) {{{import multiarray ImportError: libf77blas.so.3: cannot open shared object file}}}
#{{{./configure --prefix=$(pwd)}}}
#{{{make}}}
#{{{make install}}}
#{{{virtualenv Desktop/example -p /home/peter/Desktop/Python-3.7.0/Python-3.7.0/bin/python3}}}
#{{{cd example}}}
#{{{source bin/activate}}}
#{{{python -V}}}
#{{{deactivate}}}
#Install stretch lite
#(optional: install xfce-desktop-task but without most of the recommends, remove network-manager)
#install python3-pip
#{{{pip3 install opencv-python}}} will pull in numpy too
#Python-reported library errors and the (apt) installs you need:
##libf77blas - libatlas-base-dev
##libjasper - libjasper-dev
##libavformat - libavformat-dev
##libswscale - libswscale-dev
##libQtGui - libqtgui4
##libQtTest - libqttest4-perl
Easiest way is to put a call to the script in {{{/etc/rc.local}}}
Put the output into a log file using {{{script & > logfile 2>&1}}}
The raspberry pi has changed from using /etc/network/interfaces to using /etc/dhcpcd.conf see https://raspberrypi.stackexchange.com/questions/39785/dhcpcd-vs-etc-network-interfaces for a really good answer. 

For """WiFi""" you need to write details into /etc/wpa_supplicant/wpa_supplicant.conf, e.g.:
{{{
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
    ssid="homenetwork"
    psk="h0mepassw0rd"
}
}}}

This is how you put a static IP address into /etc/dhcpcd.conf:
{{{
# Custom static IP address for eth0.
interface eth0
static ip_address=192.168.0.115/24
static routers=192.168.0.1
static domain_name_servers=192.168.0.1

# Custom static IP address for wlan0.
interface wlan0
static ip_address=192.168.0.115/24
static routers=192.168.0.1
static domain_name_servers=192.168.0.1
}}}
The Yoga 3 with kubuntu 19.10 disables the trackpad on suspend and fails to re-enable it on resume. Seems to be a timing issue. Following [[Doing something after a resume from suspend]] create a post-resume process which calls a script:
{{{
$ cat /usr/local/bin/enable-trackpad.sh 
#!/bin/bash
TP_DEV=$(DISPLAY=:0 XAUTHORITY=/home/sarah/.Xauthority xinput --list | grep Synaptics | sed 's/.*id=\([0-9]*\).*/\1/')
echo "Enabling Synaptics, device $TP_DEV"
sleep 2
DISPLAY=:0 XAUTHORITY=/home/sarah/.Xauthority xinput enable $TP_DEV
}}}
Problem: there was a small problem with a kubuntu iso, namely missing mmx64.efi All that needed doing was to copy the grubx64.efi file in the EFI/BOOT directory under the new name. Trouble was that changing the iso with isomaster removed all the partition information so Startup Disk Creator wouldn't recognise it any more. Putting the iso onto a USB stick gives a read-only filesystem so no joy there.

Solution: manually create a boot USB drive:
#Use gparted to put a new gpt (NOT msdos) partition table on the drive
#Create a fat32 partition at least big enough for the iso
#Create any other partitions that might be useful, e.g. for permanent storage when using as a live system
#Unplug and re-plug the USB stick, mount the fat32 partition
#Mount the iso {{{sudo mount kubuntu*.iso mnt}}}
#Copy the files to the fat32 partition {{{sudo rsync -a mnt/ /wherever/the/fat32/is/mounted}}}
#Make any changes you like to the files

That's all I needed to do. I didn't mark the fat32 partition as boot or anything like that. Worked on Dell Latitude even with secure boot enabled.
There isn't a very easy interface for setting up bouquets (favourites lists) on enigma2 / VIX boxes so I've written some scripts. The process is:
*Make a temp directory
*Download the stuff from the box (assuming you've already been through this at least once, otherwise grab README.txt and the scripts below)
*Follow the instructions in README.txt to make new bouquets
*Upload the stuff back again
*Poke the box to get it to reload

Here are the magic files:
*Command to download the stuff from the box: {{{rcp -r root@192.168.1.20:/etc/enigma2 .}}}
*README.txt
{{{
$ cat README.txt 
Workflow:

1) Get enigma2 dir from enigma box

$ rcp -r root@192.168.1.20:/etc/enigma2 .

2) Create a starting point bouquets-def.csv (or use the current one)

$ cd enigma2
$ bash ./bouquets-maker.sh

This will create a vanilla bouquets-def.csv in new/ which you move to
the current directory to keep it from being overwritten in the next run.
Note particularly if there are new channels. They will be at the foot of
the new csv file in bouquet z unless you change this.

3) Edit the bouquets-def.csv file (e.g. with LibreOffice Calc) to your liking

(The structure of the csv file is documented in bouquets-maker.sh)

a) Define all the bouquets that you want

b) In column H (field 8) of the channel records put a few sort overrides, 
for example I put 1,2,3,4 for BBC1, BBC2, ITV, Channel 4
You may want to put in at least as many as you can see at once in your enigma2 skin (13 for me)
Put 100 in all the others to make sure they sort below the ones you've marked up

c) Sort the channel records in the csv file as you want them sorted
e.g. sort by column H then F (sort override, name)

d) Save the csv file, preserving its format as a csv with colon-separated fields

4) Run bouquets-maker.sh again to produce the bouquets

$ bash ./bouquets-maker.sh

This will create a directory ./new/ with all the bouquets and a copy of the csv file, scripts and this file
There shouldn't be any NEW channels this time.

5) Upload the files to the enigma (make sure to use binary mode or it will add crlf line endings)

bash ./bouquets-transfer.sh

6) Poke the enigma to get it to load the new bouquets

bash ./bouquets-reload.sh
}}}
*bouquets-maker.sh which processes the input files to make bouquet files. Mostly a simple awk script.
{{{
$ cat bouquets-maker.sh
#!/bin/bash
# Processes a bouquet definition file (bouqdefs.csv) and lamedb5
# Produces directory (new) with new definition file, bouquet files 
# and bouquet list (bouquets.tv)
# Changes:
# 180102 - copy README.txt and script files to enigma as well
#
if [ ! -e "lamedb5" ]; then
	echo "ERROR: lamedb5 not found"
	exit 1
fi
bdefs="bouquets-def.csv"
if [ ! -e $bdefs ]; then
	echo "WARNING: $bdefs does not exist, starting from scratch"
	bdefs="/dev/null" # Give awk something to read
fi

mkdir -p new # Directory for all the output files
# Copy the scripts etc in case they get lost 
cp README.txt *.sh new/

#FILE FORMATS
#Bouquet definition file
#b:<bouquet tag>:<bouquet long name>
#f1      2               3
#c:<seq*>:<lcn1*>:<lcn2*>:<type dec*>:<name>:<btag>:<u1>:<u2>
#f1  2       3       4        5          6      7     8     9
#* = automatically filled / overwritten. Used to spot changes.
# u1,2 are user fields for notes, sorting info etc. Ignored
#
#lamedb5 file
#s:5b00:eeee0000:5040:233a:12:0:0,"Channel name"
#s:<lcn1>:<eee>:<lcn2>:<aaa>:<type>:0:0,”<name>”.*
#f1   2     3     4      5        6     7     8
#
#Bouquet file
##SERVICE 1:0:C:5B00:5040:233A:EEEE0000:0:0:0:
##SERVICE 1:0:<type(hex)>:<lcn1>:<lcn2>:<aaa>:<eee>:0:0:0:
#f1         2     3         4      5     6     7
#

# ARRAY formats
# chns::= <name>[,PARAM] # internal storage of channel info
# PARAM::= <lcn1>|<lcn2>|<type>|<btag>|<aaa>|<eee>
# chnseq::= <seq>,<name> # to output to the 'all tv' bouquet in the right order
# bouqs::=<btag>,<name> # store name for bouquet file header

# Process the bdefs file (if any) and then the lamedb5 file
awk -F: '\
BEGIN {
	newcsv="new/bouquets-def.csv"
	newbtv="new/bouquets.tv"
	ubprefix="new/userbouquet."
	seq=0; bseq=0
}
#Load the first file (bouquet-def)
$1=="h" { # records beginning h are header comments, transfer to new file
		print >newcsv
}
$1=="b" { # records beginning b are bouquet definitions
	# $2 is the bouquet tag c,p etc $3 is for the name definition in the bouquet file
	bouqs[$2]=$3
	bouqseq[++bseq]=$2 # track initial ordering to preserve in output
}
$1=="c" { # records beginning with b are channel definitions
	chns[$6]=++seq; 
	chnseq[seq]=$6;
	chns[$6,"lcn1"]=$3;
	chns[$6,"lcn2"]=$4;
	chns[$6,"type"]=$5;
	chns[$6,"btag"]=$7;
	chns[$6,"u1"]=$8;
	chns[$6,"u2"]=$9;
	if (!($7 in bouqs)) {
		print "Bouquet tag not defined: " $0
	}
}
# this matches in lamedb5
$1=="s" { # service records begin with s
	#There is 0," before the name and " followed by arbitrary rubbish at the end
	split($8,namea,"\"")
	newn=namea[2];
	dupchan=false;
	if (newn in chns) { # existing channel, see if anything has changed	
		if ($2==chns[newn,"lcn1"] && $4==chns[newn,"lcn2"] && $6==chns[newn,"type"]) {
			#print "Existing channel unchanged: " newn
		}
		else { # somethings changed
			if (chns[newn,"aaa"]=="") { # not seen in lamedb5 yet
				print "* UPDATED: " newn " - lcn1:" chns[newn,"lcn1"] "/" $2 ", lcn2:" chns[newn,"lcn2"] "/" $4 ", type:" chns[newn,"type"] "/" $6;
			}
			else {
				print "= DUPLICATE IGNORED: " newn " - lcn1:" chns[newn,"lcn1"] "/" $2 ", lcn2:" chns[newn,"lcn2"] "/" $4 ", type:" chns[newn,"type"] "/" $6;
				dupchan=true
			}
		}
	}
	else { # new channel, add to chns and mark bouquet as new
		print "+ NEW: " newn;
		chns[newn]=++seq; 
		chnseq[seq]=newn;
		chns[newn,"btag"]="new";
		if (!("new" in bouqs)) {
			bouqs["new"]="New Channels";
		}
	}
	if (!dupchan) {
		chns[newn,"lcn1"]=$2;
		chns[newn,"lcn2"]=$4;
		chns[newn,"type"]=$6;
		chns[newn,"aaa"]=$5;
		chns[newn,"eee"]=$3;
	}
}
END	{
	# Put headers in all the bouquet files
	ball=ubprefix "all.tv"
	print "#NAME All TV channels" > ball
	for (i=1;i<=bseq;i++) { # print the headers for all the bouquet files
		btag=bouqseq[i]
		bname=ubprefix btag ".tv" # bouquet file name
		print "#NAME " bouqs[btag] > bname
		# Print b: records into updated defs file
		print "b:" btag ":" bouqs[btag] >newcsv
	}
	# Now print the contents of the bouquet files
	for (i=1;i<=seq;i++) { 
		chnname=chnseq[i]
		if (chns[chnname,"aaa"]=="")
			print "- DELETED:" chnname
		else {
			bline=sprintf ("#SERVICE 1:0:%x:%s:%s:%s:%s:0:0:0:", 
				chns[chnname,"type"], 
				chns[chnname,"lcn1"], 
				chns[chnname,"lcn2"], 
				chns[chnname,"aaa"], 
				chns[chnname,"eee"])
			print bline >ball
			print bline >ubprefix chns[chnseq[i],"btag"] ".tv" # e.g. userbouquet.p.tv
			# Also print an updated version of the c: records
			printf "c:%d:%s:%s:%s:%s:%s:%s:%s\n",
				i,
				chns[chnname,"lcn1"],
				chns[chnname,"lcn2"],
				chns[chnname,"type"],
				chnname,
				chns[chnname,"btag"],
				chns[chnname,"u1"],
				chns[chnname,"u2"] >newcsv
		}
	}
	# Now print a new version of the bouquets.tv file with all bouquets in
	print "#NAME Bouquets (TV)" >newbtv
	for (i=0;i<=bseq;i++) { # preserve the ordering from the def file
		if (i==0) btag="all"; else btag=bouqseq[i] # all tv is first in list
		bname="userbouquet." btag ".tv" # bouquet file name
		print "#SERVICE 1:7:1:0:0:0:0:0:0:0:FROM BOUQUET \"" \
			bname "\" ORDER BY bouquet" >newbtv
	}
}' $bdefs lamedb5
}}}
*Script to transfer all the files back to the enigma2 box (in binary mode to avoid adding crlf line endings)
{{{
$ cat bouquets-transfer.sh 
#!/bin/bash
if [ -z "$3" ]; then
	echo "usage: $0 <boxip> <user> <password>"
	exit 1
fi
echo "Transferring bouquet info from $(pwd)/new to $1"
ftp -n <<EOF
open $1
quote user $2
quote pass $3
cd /etc/enigma2
lcd new
prompt n
binary
mput *
EOF
}}}
*Script to poke the enigma2 box to make it reload the bouquet files
{{{
$ cat bouquets-reload.sh 
#!/bin/bash
if [ -z "$1" ]; then
	echo "usage: $0 <boxip> <user> <password>"
	exit 1
fi
echo "Reloading bouquet info on $1"
wget -O $0.log http://$2:$3@$1/web/servicelistreload?mode=2
echo "Result:"
cat $0.log
}}}
*Example bouquets definition file
{{{
h:First char is tag, h=header comment, b=bouquet, c=channel::::::
h:bouquet definitions must come first and have three fields, b<bouquet tag><bouquet long name>::::::
h:The <bouquet tag> z is reserved for the new channels bouquet::::::
h:channel definitions have 9 fields c<seq><lcn1><lcn2><type><name><btag><u1><u2>::::::
h:the btag field links to a bouquet, u1 and u2 are free to use for e.g. sorting (e.g. sort by H, G, F)::::::
h:Bouquets appear on the box in the order below except that first will be the 'All' bouquet::::::
b:nws:News
b:del:Delayed (+1s)
b:rel:Religion
b:kid:Children's TV
b:mus:Music
b:sd:SD with HD alternative
b:shp:Shopping
b:for:Foreign
b:ent:Entertainment
b:trv:Travel
b:int:Interactive services
b:rad:Radio
b:pay:Pay
b:new:New Channels
b:no:No thank you
c:1:4484:4083:25:BBC ONE HD:ent:1:
c:2:4440:4083:25:BBC TWO HD:ent:2:
c:3:44c3:4083:25:ITV HD:ent:3:
c:4:4500:4083:25:Channel 4 HD:ent:4:
c:5:4540:4083:25:Channel 5 HD:ent:5:
c:6:a000:a000:25:BBC NEWS HD:nws:6:
c:7:20c1:200e:1:Film4:ent:7:
c:8:6500:6040:1:5Spike:ent:10:
c:9:3280:3006:1:5STAR:ent:10:
c:10:5dd0:5040:1:movies4men:ent:10:
c:11:5de0:5040:1:Sony Movie Ch:ent:10:
c:12:2085:200e:1:ITV2:ent:15:
c:13:3eb0:3006:1:ITV3:ent:15:
c:14:208a:200e:1:ITV4:ent:15:
c:15:a040:a000:25:4seven HD:ent:100:
c:16:32c0:3006:1:5 USA:ent:100:
c:17:3ed0:3006:1:5SELECT:ent:100:
c:18:b040:b000:25:BBC FOUR HD:ent:100:
c:19:4cc0:4083:25:BBC RB HD:ent:100:
c:20:3830:3006:1:Blaze:ent:100:
c:21:b050:b000:25:BT Showcase HD:ent:100:
c:22:6c60:6040:1:CBS Action:ent:100:
c:23:6c80:6040:1:CBS Drama:ent:100:
c:24:3870:3006:1:CBS Reality:ent:100:
c:25:56d2:5040:1:Challenge:ent:100:
c:26:5700:5040:1:Dave:ent:100:
c:27:6dc0:6040:1:Dave ja vu:ent:100:
c:28:3f50:3006:1:Drama:ent:100:
c:29:2100:200e:1:E4:ent:100:
c:30:b120:b000:22:Forces TV:ent:100:
c:31:b110:b000:22:FreeSports:ent:100:
c:32:3890:3006:1:Horror Channel:ent:100:
c:33:3df0:3006:1:ITVBe:ent:100:
c:34:20fa:200e:1:More 4:ent:100:
c:35:b130:b000:22:PBS America:ent:100:
c:36:56c0:5040:1:Pick:ent:100:
c:37:5ca0:5040:1:Really:ent:100:
c:38:5d00:5040:1:Sony Crime Channel:ent:100:
c:39:a120:a000:22:TalkingPictures TV:ent:100:
c:40:5a20:5040:1:TJC:ent:100:
c:41:3910:3006:1:True Entertainment:ent:100:
c:42:6ce0:6040:1:True Movies:ent:100:
c:43:5a70:5040:1:truTV:ent:100:
c:44:a1b0:a000:22:Vintage TV:ent:100:
c:45:a110:a000:22:VIVA:ent:100:
c:46:64c0:6040:1:Yesterday:ent:100:
c:47:5ab0:5040:1:YourTV:ent:100:
c:48:46c0:4083:25:CBBC HD:kid:100:
c:49:b030:b000:25:CBeebies HD:kid:100:
c:50:3ea0:3006:1:CITV:kid:100:
c:51:a200:a000:22:5Spike+1:del:100:
c:52:b090:b000:22:5STAR+1:del:100:
c:53:a180:a000:22:5USA+1:del:100:
c:54:3834:3006:1:Blaze+1:del:100:
c:55:a1e0:a000:22:CBS Action+1:del:100:
c:56:a170:a000:22:CBS Reality +1:del:100:
c:57:a030:a000:25:Channel 4+1 HD:del:100:
c:58:32e0:3006:1:Channel 5+1:del:100:
c:59:5740:5040:1:E4+1:del:100:
c:60:4f80:4083:22:Film4+1:del:100:
c:61:20b2:200e:1:ITV +1:del:100:
c:62:3e50:3006:1:ITV2 +1:del:100:
c:63:3e90:3006:1:ITV3+1:del:100:
c:64:3e30:3006:1:ITV4+1:del:100:
c:65:3df8:3006:1:ITVBe+1:del:100:
c:66:b0e0:b000:22:More4+1:del:100:
c:67:a1f0:a000:22:Pick+1:del:100:
c:68:a1d0:a000:22:Quest Red+1:del:100:
c:69:5a90:5040:1:truTV+1:del:100:
c:70:6d20:6040:1:YourTV+1:del:100:
c:71:6440:6040:1:4Music:mus:100:
c:72:a1a0:a000:22:Keep It Country:mus:100:
c:73:6640:6040:2:KISS:mus:100:
c:74:6b20:6040:1:POP:mus:100:
c:75:a050:a000:25:Al Jazeera Eng HD:nws:100:
c:76:1280:1043:1:BBC Parliament:nws:100:
c:77:a150:a000:25:RT HD:nws:100:
c:78:5640:5040:1:Sky News:nws:100:
c:79:5a50:5040:1:TBN UK:rel:100:
c:80:3898:3006:1:TCC:rel:100:
c:81:6a20:6040:1:4seven:sd:100:
c:82:11c0:1043:1:BBC FOUR:sd:100:
c:83:1100:1043:1:BBC NEWS:sd:100:
c:84:1043:1043:1:BBC ONE South:sd:100:
c:85:10bf:1043:1:BBC TWO:sd:100:
c:86:1200:1043:1:CBBC:sd:100:
c:87:1240:1043:1:CBeebies:sd:100:
c:88:20c0:200e:1:Channel 4:sd:100:
c:89:2104:200e:1:Channel 4+1:sd:100:
c:90:2134:200e:1:Channel 5:sd:100:
c:91:204e:200e:1:ITV:sd:100:
c:92:6b40:6040:1:RT:sd:100:
c:93:6c20:6040:1:Travel Channel:trv:100:
c:94:3ae0:3006:12:Asia TV:for:200:
c:95:39c0:3006:12:365 Travel:shp:200:
c:96:5f40:5040:1:Create & Craft:shp:200:
c:97:5a00:5040:1:Food Network:shp:200:
c:98:5f80:5040:1:Gems TV:shp:200:
c:99:384c:3006:1:Hochanda:shp:200:
c:100:6d60:6040:1:Home:shp:200:
c:101:6540:6040:1:Ideal World:shp:200:
c:102:6a00:6040:1:Jewellery Maker:shp:200:
c:103:38a2:3006:1:QUEST:shp:200:
c:104:3868:3006:1:QUEST+1:shp:200:
c:105:3340:3006:1:QVC:shp:200:
c:106:b010:b000:25:QVC Beauty HD:shp:200:
c:107:b000:b000:25:QVC HD:shp:200:
c:108:6e60:6040:1:QVC Style:shp:200:
c:109:b100:b000:22:Rocks & Co 1:shp:200:
c:110:6a40:6040:1:Sewing Quarter:shp:200:
c:111:38c4:3006:1:The Store:shp:200:
c:112:6e80:6040:1:QVC Beauty:sd:200:
c:113:3b68:3006:1:695:int:300:
c:114:3b48:3006:1:696:int:300:
c:115:5fb4:5040:1:697:int:300:
c:116:6f68:6040:1:698:int:300:
c:117:1c00:1043:1:BBC RB 1:int:300:
c:118:1140:1043:1:BBC Red Button:int:300:
c:119:5cc0:5040:1:Quest Red:int:300:
c:120:3960:3006:2:Absolute Radio:rad:400:
c:121:1680:1043:2:BBC 6 Music:rad:400:
c:122:1740:1043:2:BBC Asian Net.:rad:400:
c:123:1883:1043:2:BBC Berkshire:rad:400:
c:124:1700:1043:2:BBC R1X:rad:400:
c:125:1600:1043:2:BBC R5L:rad:400:
c:126:1640:1043:2:BBC R5SX:rad:400:
c:127:1a40:1043:2:BBC Radio 1:rad:400:
c:128:1a80:1043:2:BBC Radio 2:rad:400:
c:129:1ac0:1043:2:BBC Radio 3:rad:400:
c:130:1b00:1043:2:BBC Radio 4:rad:400:
c:131:16c0:1043:2:BBC Radio 4 Ex:rad:400:
c:132:1803:1043:2:BBC Solent:rad:400:
c:133:1843:1043:2:BBC Solent Dorset:rad:400:
c:134:184c:1043:2:BBC Sussex:rad:400:
c:135:1881:1043:2:BBC Wiltshire:rad:400:
c:136:1780:1043:2:BBC World Sv.:rad:400:
c:137:39a0:3006:2:Capital:rad:400:
c:138:6600:6040:2:Classic FM:rad:400:
c:139:3980:3006:2:Heart:rad:400:
c:140:6680:6040:2:heat:rad:400:
c:141:66c0:6040:2:Kerrang!:rad:400:
c:142:69a0:6040:2:KISS FRESH:rad:400:
c:143:6880:6040:2:KISSTORY:rad:400:
c:144:65c0:6040:2:LBC:rad:400:
c:145:6800:6040:2:Magic:rad:400:
c:146:6e00:6040:2:Premier Radio:rad:400:
c:147:5860:5040:2:RNIB Connect:rad:400:
c:148:6700:6040:2:Smooth Radio:rad:400:
c:149:5840:5040:2:talkSPORT:rad:400:
c:150:67c0:6040:2:The Hits Radio:rad:400:
c:151:a0d0:a000:2:Trans World Radio:rad:400:
c:152:6d00:6040:1:ADULT Babestn:no:500:
c:153:3c98:3006:1:ADULT Party:no:500:
c:154:6bc0:6040:1:ADULT Section:no:500:
c:155:6bc0:6040:1:ADULT Section:no:500:
c:156:6b80:6040:1:ADULT smileTV2:no:500:
c:157:5780:5040:1:ADULT smileTV3:no:500:
c:158:38f0:3006:1:ADULT Studio 66:no:500:
c:159:38d0:3006:1:ADULT Xpanded TV:no:500:
c:160:b140:b000:22:Together:no:500:
c:161:4cc0:4083:100:474000 SID 0x4cc0:new:500:
c:162:210c:200e:100:522000 SID 0x210c:new:500:
c:163:3c48:3006:12:Arise News:pay:600:
c:164:a160:a000:12:Bollywood HD:pay:600:
c:165:3850:3006:12:Box Nation:pay:600:
c:166:3ac8:3006:12:CCTV:pay:600:
c:167:3ad0:3006:12:CONNECT 4:pay:600:
c:168:2198:200e:12:Freeview Information:pay:600:
c:169:3f48:3006:12:Ketchup TV:pay:600:
c:170:5af0:5040:12:Kiss Me TV:pay:600:
c:171:3a98:3006:12:Loveworld:pay:600:
c:172:3ad8:3006:12:Planet Knowledge:pay:600:
c:173:5b00:5040:12:Proud Dating:pay:600:
c:174:3ab8:3006:12:Racing UK:pay:600:
c:175:3858:3006:12:Rocks and Co:pay:600:
c:176:3aa8:3006:12:Sonlife:pay:600:
c:177:3c68:3006:12:Sports Channel Network:pay:600:
c:178:3b80:3006:12:Television X:pay:600:
c:179:3c60:3006:12:VisionTV:pay:600:
}}}
#{{{aptitude --disable-columns -F "%p" search "~i~n^fonts-|~i~n^ttf-"}}} will tell you which font packages are installed.
#Save the list into a file (e.g. {{{tempfile}}}) and edit to leave just the packages you want purged.
#Purge them using sed to get all the lines of the file into one argument list: {{{sudo aptitude purge $(sed ':nl N; $ ! b nl; s/\n/ /g' tempfile)}}}
My list was: 
{{{
sudo aptitude purge fonts-horai-umefont fonts-kacst fonts-kacst-one fonts-khmeros-core fonts-lao fonts-lklug-sinhala fonts-lyx fonts-nanum fonts-sil-abyssinica fonts-sil-padauk fonts-takao-pgothic fonts-thai-tlwg fonts-tibetan-machine fonts-tlwg-garuda fonts-tlwg-kinnari fonts-tlwg-loma fonts-tlwg-mono fonts-tlwg-norasi fonts-tlwg-purisa fonts-tlwg-sawasdee fonts-tlwg-typewriter fonts-tlwg-typist fonts-tlwg-typo fonts-tlwg-umpush fonts-tlwg-waree fonts-unfonts-core fonts-wqy-microhei ttf-indic-fonts-core ttf-punjabi-fonts ttf-wqy-microhei
}}}
#Go to https://myaccount.google.com/security 
#Click on {{{Sign-in & security}}}
#Click on {{{REVIEW DEVICES}}}
#Click on the device you want to remove
#Click the big red ''@@bgcolor(#ff0000):color(#ffffff):REMOVE@@'' button
Secure Settings plugin for Tasker doesn't work with systemless root and is not maintained. This website shows commands to get around this problem.
https://www.reddit.com/r/tasker/comments/4goz99/how_to_replace_some_secure_settings_actions_with/
In case it goes away, here's the important stuff. All {{{Shell Commands}}} must be run with the {{{Use Root}}} box ticked.
*Enable Airplane Mode
**Run Shell Command: {{{settings put global airplane_mode_on 1; am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true}}}
*Disable Airplane Mode
**Run Shell Command: {{{settings put global airplane_mode_on 0; am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false}}}
*Enable Mobile Data
**Run Shell Command: {{{svc data enable}}}
*Disable Mobile Data
**Run Shell Command: {{{svc data disable}}}
*Enable Battery Saver
**Run Shell Command: {{{settings put global low_power 1}}}
*Disable Battery Saver
**Run Shell Command: {{{settings put global low_power 0}}}
*Enable """Wi-Fi""" "scanning always available"
**Run Shell Command: {{{settings put global wifi_scan_always_enabled 1}}}
*Disable """Wi-Fi""" "scanning always available"
**Run Shell Command: {{{settings put global wifi_scan_always_enabled 0}}}
*Check if """Wi-Fi""" "scanning always available" is on/off
**Run Shell Command: {{{settings get global wifi_scan_always_enabled}}}
***Store Output In: {{{%wifiscanning}}}
**Flash
***Text: {{{%wifiscanning }}}
***Long: {{{Off}}}
*Get Current Location Mode
**Run Shell Command: {{{settings get secure location_providers_allowed}}}
***Store Output In:%location 
**Flash
***Text: {{{%location}}}
***Long: {{{Off}}}
*List Enabled Accessibility Services
**Run Shell Command: {{{settings get secure enabled_accessibility_services}}}
***Store Output In: {{{%accessibility}}}
**Flash
***Text: {{{%accessibility}}}
***Long: {{{On}}}
*Enable An Accessibility Service
**Run Shell Command: {{{settings get secure enabled_accessibility_services}}}
***Store Output In: {{{%accessibility}}}
**Run Shell Command: {{{settings put secure enabled_accessibility_services %accessibility:com.joaomgcd.autoinput/com.joaomgcd.autoinput.service.ServiceAccessibility}}}
*List Enabled Notification Listeners
**Run Shell Command: {{{settings get secure enabled_notification_listeners}}}
***Store Output In: {{{%nlisteners}}}
**Flash
***Text: {{{%nlisteners}}}
***Long: {{{On}}}
*Enable A Notification Listener
**Run Shell Command: {{{settings get secure enabled_notification_listeners}}}
***Store Output In: {{{%nlisteners}}}
**Run Shell Command: {{{settings put secure enabled_notification_listeners %nlisteners:com.joaomgcd.autonotification/com.joaomgcd.autonotification.service.ServiceNotificationIntercept}}}
These settings are a subset of those listed in the Android docs at: 
*https://developer.android.com/reference/android/provider/Settings.Global.html
*https://developer.android.com/reference/android/provider/Settings.Secure.html
*https://developer.android.com/reference/android/provider/Settings.System.html
*Also try {{{svc help}}}
Problem: extrusion jam etc results in print stopping half way through. If it's not messed up you can continue from where it got to.
This is difficult because:
#GPX doesn't do a straight 1:1 conversion of your gcode so it's slightly magical getting the right form of gcode
#The extrusion amounts are absolute so you can't just start half way through a gcode file or you get filament everywhere!
This is what you do:
#Measure where you've got to, e.g. 3.3mm
#Find the next layer UP in """Slic3r""", e.g. 3.5mm. This is the continuation layer.
#Get the gcode file and make a copy, say cont.gcode
#Edit cont.gcode and find the continuation layer by searching for its Z value, e.g. Z3.5
#Delete everything from the start of the 'Before layer' code for the continuation layer to the start of the first layer (first layer starts with {{{M73 P0}}})
#Change the 'Before layer' code as follows:
##Leave the M73 in unchanged
##Note the starting E (extrusion) value for the continuation layer
##Add a G1 to set the X, Y and Z. If you don't have this G1, GPX will put zeroes into the G92 for X and Y, and override your machine's offsets. Make sure the X and Y don't cross the model!
##Then add a G92 for the continuation layer's extrusion value (first E value in the continuation layer)
#Save the modified gcode file and run gpx in the same way you do in Slic3r to get the x3g file
#Make sure to prime the extruder before printing
Here's a snippet from one that worked for me, lines marked * are unchanged from the original gcode file:
{{{
; Filament gcode*
G21 ; set units to millimeters*
G90 ; use absolute coordinates*
;--- Before layer change*
M73 P20*
G1 X-100 Y75 Z3.5 F3600 <----- Z is layer height for continuation layer, X&Y are near home
G92 E3145.00177         <----- copy E value from the first one used in the layer
                        <----- retraction code etc removed
;---After layer change*
G1 X67.191 Y3.989 F3600.000*
G1 E3145.00177 F2400.00000*
}}}
Daniel's bug report: http://comments.gmane.org/gmane.comp.freedesktop.xorg.drivers.intel/36914
kernels: http://kernel.ubuntu.com/~kernel-ppa/mainline/drm-intel-nightly/
Report at freedesktop bugs: https://bugs.freedesktop.org/buglist.cgi?product=DRI&component=DRM%2FIntel&resolution=---
If you want to stream one of your """DVDs""" around the home, the first step is to rip it to a hard drive. Two methods seem reliable:
#VLC
##First find the 'title' number you want. In VLC click on Playback / Title and the one you want is almost certainly the longest.
##Now go to Media / Convert and enter the title number found above.
##Set up the destination for the ripped video. 
##Default """MP4""" takes about 7Mb per minute (30MB per hour) of the original video.
##Ripping takes about 2 to 3 times the original running time (4 core 1.2 Gig processor)
#Handbrake
##Much easier and about twice as quick
##For storing on a computer: use the {{{Regular/High Profile}}} and maybe tweak the audio track to just use the passthru one and select subtitles if required
##See [[Handbrake quality settings]]
''BACKUP device first''
#Switch off
#Get into bootloader: hold vol up, vol down and power
#Use vol up and vol down and power to select recovery
#backup everything

On PC - download everything you need
#install adb and fastboot: {{{aptitude install android-tools-adb android-tools-fastboot}}}
#Download """ClockworkMod""" recovery from http://www.clockworkmod.com/rommanager/ (I used the touch version)
#Download any ROM required
#Download matching gapps if required
#copy files to device (in xubuntu 13.10 the MTP packages are installed by default and the Nexus4 shows up as a device in Thunar)
#copy backup FROM device onto PC just in case you need to format the SD card

On device - enable USB debugging
#Enable USB debugging: 
##In About phone setting, tap 7 times on build number to enable Developer options
##Go back to find Developer options and tick USB debugging
##First time a command is issued need to OK your PC's id
#Get into bootloader: either switch off then hold vol up, vol down and power; or (On PC) {{{adb reboot bootloader}}}

On PC - unlock the bootloader and flash new recovery
#Unlock the bootloader: {{{sudo fastboot oem unlock}}} Should give a confirmation dialog saying all data will be wiped
#Flash new recovery: {{{sudo adb flash recovery path/to/downloaded/recovery.img}}}

On device - install new ROM and reboot
#Select Recovery using Vol up and Power to get into CWM recovery
#Wipe everything (I had to format /data too)
#Backup the existing ROM
#Install ROM and gapps
#Reboot device asking for it to be rooted when prompted
#Wait a very long time for the first boot
#Press Power and Vol down to take screen shot and send to friends
Using an xfce panel launcher normally ({{{gksudo aptitude}}}) will fire up aptitude and as soon as it builds its view will close the terminal window. Here is the command to make it behave as you would expect (the {{{--geometry}}} is not needed, just gives a bigger window)
{{{gksudo "xfce4-terminal --geometry=100x50 -H -x aptitude"}}}
The """CLP-620ND""" has two issues on xubuntu 12.10:

#Can't print landscape from """LibreOffice""", instead it prints a truncated portrait page.
#Prints one page fine then the next will be a pile of paper with a short line of garbage (letters and symbols) at the top of each page.

Neither of these problems is anything to do with the driver! The latest driver works fine with these two workarounds:

The first is caused by the fact that """LibreOffice""" has a new default printer language of PDF. Change this (in Printer / Properties / Device / Printer language type) to """PostScript""" (Level from driver). This problem caused by the pdftopdf filter not autorotating the PDF which """LibreOffice""" sends to CUPS, the fix avoids that filter.

The second issue is because CUPS isn't finishing the USB transaction properly. You can either switch off the printer after each job (!) or you can use this pair of commands:
{{{
lpadmin -p Samsung-Laser -R usb-unidir-default
lpadmin -p Samsung-Laser -o usb-no-reattach-default=true
}}}
This lasts across reboots. Also see https://bugs.launchpad.net/ubuntu/+source/cups/+bug/872483 for full details of this, and my comment 63 which is for the Samsung.
Need to change the device URI in /etc/cups/printers.conf. {{{hp-makeuri <IP address>}}} will tell you the URI to use, e.g.
{{{
DeviceURI hp:/net/HP_Officejet_Pro_X576dw_MFP?ip=192.168.1.101
}}}
(It was previously {{{socket://192.168....}}})
Restart cups: {{{sudo systemctl restart cups.service}}}

xsane will then be able to auto detect the scanner.
To check:
{{{
~$ scanimage -L
<lots of rubbish>
device `hpaio:/net/HP_Officejet_Pro_X576dw_MFP?ip=192.168.1.101' is a Hewlett-Packard HP_Officejet_Pro_X576dw_MFP all-in-one
}}}
{{{
~$ cat $(which apt-history)
#!/bin/bash
dpkg --get-selections > /home/peter/dpkg--get-selections # Used later to see if packages are still installed
zcat /var/log/apt/history.log.* | 
awk ' \
/^Start-Date/ {
	dat=$2
}
/^Requested-By:|^Commandline/ {
	req=$2
}
/^Install:.*[^c]).*/ { # at least one entry not automtically installed
	i=split ($0, entries, "Install: |), |)$") # first, middle, last entries
	for (i=i-1; i>1; i--) { # First and last entries are rubbish
		if (entries[i] !~ "automatic$") {
			entry=entries[i]
			split (entry, fields, "[(]|:") # Fields: package name; i386 etc; version
			if (system("grep \"^" fields[1] "[[:space:]]*install\" /home/peter/dpkg--get-selections >/dev/null") == 0) # check if still installed
				mark="";
			else
				mark="D" # No longer with us
			printf "%10s %s: %2s %-25s %10s %s\n", dat, req, mark, fields[1], fields[2], fields[3]
		}
	}
}
 ' |
sort -k1 # sort by date (k3 is by package name)

}}}

Example output:
{{{
2017-09-11 peter:   D feh                          amd64  2.18-1
2017-09-11 peter:   D mirage                       amd64  0.9.5.2-1
2017-09-11 peter:     nomacs                       amd64  3.4.1+dfsg-5build1
2017-09-16 peter:   D qt4-qtconfig                 amd64  4
2017-09-16 peter:     systemsettings               amd64  4
2017-09-19 peter:     gddrescue                    amd64  1.21-1
2017-09-19 peter:     libgdk-pixbuf2.0-dev         amd64  2.36.5-3ubuntu0.2
2017-09-29 peter:     cheese                       amd64  3.24.0-0ubuntu1
}}}
In Linux {{{<alt>click-drag}}} moves the whole window instead of selecting text. So to select text within a hyperlink you need to use {{{<ctrl><alt>click-drag}}} which works for normal text too.
Another solution is to position the cursor one pixel (not two or you select the line above) above the hyperlink and click-drag from there.
#ssmtp no longer works with raspbian buster
#install msmtp (don't need msmtp-mta)
#{{{msmtp --configure xxx@gmail.com}}}
#This gives a proposed contents for {{{.msmtprc}}}
#Create {{{.msmtp}}} and copy and paste the output
#Change the account name to be {{{default}}}
#Unless you have gpg set up change the passwordeval line to {{{password mygmailpw}}}
#Now contains secrets so secure the file: {{{chmod 600 .msmtprc}}}
#Try it out: {{{echo "test msmtp" | msmtp "abc@example.com"}}}
#Note: some help sites say you need /etc/aliases. I don't have one.
{{{
$ cat .msmtprc 
account default
host smtp.gmail.com
port 587
tls on
tls_starttls on
auth on
user xxx
password xxxpassword
from xxx@gmail.com
}}}
Sending more complex mail:
#Set up a headers.txt file
#Strange thing is that you have to specify the to address in the command line as well as in the headers.txt file. It's delivered to the one on the command line but the email says it's been sent to the one in the headers file
#You can do some magic to copy the destination from the headers file: {{{cat msmtp-header.txt msmtp-body.txt | msmtp "$(grep To msmtp-header.txt | sed "s/To: //")"}}}
#Example headers file (note blank line before message text begins)
{{{
To: aaa@gmail.com, bbb@gmail.com
From: xxx@gmail.com
Subject: Testing MSMTP

}}}
EPSON scanners now (Xubuntu 16.04) seem to work out of the box; used to have to do this:
download iscan packages from http://download.ebz.epson.net/dsc/search/01/search/searchModule
{{{
sudo dpkg -i iscan-data*.deb
sudo dpkg -i iscan_*ltdl7*.deb
sudo adduser peter lp
}}}
If this doesn't work (try {{{sane-find-scanner}}}) you may need to edit /etc/sane.d/dll.conf
*Download the factory image from LEDE site (replaces """OpenWRT""")
*Install to """WRT1200AC""" using Linksys interface
*Start up, annoyingly LEDE has the router come up at 192.168.1.1 so have to connect directly using cable
*Adjust
**root password
**time zone
**LAN address, gateway etc
**Wireless """SSIDs""", passwords etc
*Change the router to act as a single switch to link everything together
**Network/Interfaces: Delete the {{{WAN}}} and {{{WAN6}}} interfaces
**Network/Switch: put untagged into all entries in VLAN 1, delete VLAN 2, uncheck enable VLAN functionality
*Reboot and check it works
*Install disk and samba stuff
**{{{ssh root@192.168.1.xx}}}
**{{{# opkg install kmod-usb-storage block-mount samba36-server luci-app-samba}}}
**{{{# mkdir-p /nas/media; chmod 777 /nas/media}}}
**Permissions on LEDE box will be important
**Use gparted on a Linux PC to give the nas drive partitions sensible labels (LEDE gives you a menu based on the labels)
**Plug in USB drive to router
*Configure samba through """LuCi"""
**New menu item System/Mount points: set up USB disk partitions to mount onto /nas/media, check enable box
**New menu item Services/Network shares: set up sharing of /nas/media, start by allowing guest access
*Test
**On a PC check via ssh that the disk is mounted, reboot LEDE if not.
**Use Thunar to go to {{{smb://192.168.1.xx}}} or browse Windows file system
*Set up smb users in LEDE and non-guest access
**Change mount in Services/Network shares to user access rather than guest
**ssh into LEDE
**Add user to /etc/passwd: {{{echo "peter:*:1000:1000:peter:/nas:/bin/false" >>/etc/passwd}}}
**Add samba user password: {{{smbpasswd -a peter}}}
**Reboot the LEDE box
*Set up automatic mount on client PC
**Install {{{cifs-utils}}}
**Test you can mount, e.g. {{{sudo mount -o guest //192.168.1.4/media mnt; ls mnt}}} (needs guest access enabled)
**Put lines into fstab: {{{//192.168.1.4/media /Disks/Archive cifs rw,username=peter,password=naspw,nofail 0 2
**reboot and test
#Download Raspbian sketch lite from https://www.raspberrypi.org/downloads/raspbian/
#Download Etcher from https://etcher.io/ (no need to install as it's an """AppImage""")
#Transfer image to SD card using Etcher
#Plug HDMI cable into """RPi"""
#Plug Logitech unifying receiver into """RPi"""
#Boot up """RPi""" with Raspbian image
##Answer questions about timezone etc, log into """WiFi""" network
##{{{sudo raspi-config}}}
###Switch on ssh access
###Switch on vnc access
###Set up WiFi access
###Change the default password from raspberry
##{{{sudo aptitude}}}
###Check for and install updates
##{{{ifconfig}}} and note IP address - then do the rest via ssh from external PC
#Install desktop of your choice, e.g. xfce-desktop-task, checking the 'recommends' for stuff you don't need such as LibreOffice
#On PC download and install """RealVNC""" viewer from https://www.realvnc.com/en/connect/download/viewer/
#{{{vncviewer}}} and enter details of the """RPi"""
#To copy files back from the """RPi""" use scp, eg: {{{scp pi@192.168.1.xxx:Desktop/test.py .}}} or the VNC viewer.
(Also see [[Controlling WS2812 LED strip with RPi]])
#Download and install latest raspbian image (use copy-to-SD.sh in Distros)
#Log in and initial setup using HDMI and keyboard/mouse
#Set up """RealVNC""" (click on server icon top right and input license details)
#Update everything
#Clone the two repos into Desktop: 
{{{
cd /home/pi/Desktop
git clone https://peterthevicar@github.com/peterthevicar/ws2812-animator ws2812-animator
git clone https://peterthevicar@github.com/peterthevicar/STC-Lights STC-Lights
ln -s STC-Lights/support s
}}}
#Disable sound module which would use the same output: {{{sudo su}}} then {{{echo "blacklist snd_bcm2835" >/etc/modprobe.d/blacklist-snd-bcm2835.conf; exit}}} then reboot
#Install requirements {{{sudo apt install python3-rpi.gpio python3-numpy}}}
#Install stuff from pip {{{sudo pip3 install rpi_ws281x pyusb udmx-pyusb}}}
#Patch ws281x library as per [[Controlling WS2812 LED strip with RPi]] (fixed version in ws2812-animator dir) {{{sudo cp ws2812-animator/fixed-rpi_ws281x.py  /usr/local/lib/python3.7/dist-packages/rpi_ws281x/rpi_ws281x.py}}}
#To autostart the lights, put this at the end of /etc/rc.local: {{{/home/pi/Desktop/STC-Lights/support/lights &}}}
#Add any extra wifi networks required {{{sudo geany /etc/wpa_supplicant/wpa_supplicant.conf}}} and copy the network paragraph from the existing connection with required changes
#For repeater:
##Disable dmx in """RPi-controller/dmx.py"""
##Set number of """LEDs""" and brightness in lights-main.py (a few edits for the latter)
##Disable error reporting in s/lights
Installing kdenlive has by default very small fonts for the menu bar and other labels. The xfce settings have no effect. To fix this install systemsettings and run it from a terminal (it doesn't seem to work launching it from the xfce settings panel even though it's there). Adjust the fonts, restart kdenlive and all should be well.
[[TiddlySpot|http://tiddlyspot.com]]&nbsp;&nbsp;&nbsp; <<newTiddler>> &nbsp;&nbsp;&nbsp;<<newJournal>>
Peter's Notes
#It doesn't need any drivers to work with Xubuntu 14.04, they're already installed.
#The build table support wasn't level when it arrived. I THINK it was being distorted by the levelling nuts being done up too tightly. Anyway some combination of 'return to home axes' and undoing the wing-nuts seemed to straighten it out.
#Jammed filament (!) can be freed by selecting 'Load Filament', pushing it through till it's flowing well then 'Unload Filament' and pull it gently out. Repeat as necessary!
#Calibrating the bed needs the piece of (80gsm) paper to be really tightly held, not just slightly. The first layer has to be pushed into the bed rather than laid on top.
#The two extruders weren't at the same level. With this model they just push in and out so I wiggled them with a spanner while pushing them into the heating coils as far as they would go and they ended up level

Using Sailfish firmware: 
#go to http://www.thingiverse.com/thing:32084/#files and download the latest replicatorg package for linux
#Unpack somewhere, cd to that directory and run {{{./replicatorg}}}
#In replicatorg select Machine/Machine type (Driver)/The Replicator dual (sailfish)
#Select Machine/Connection (Serial port)/Rescan serial ports
#Select Machine/Connection (Serial port)/dev/ttyACM0 (or whatever)
#Click the Connect button and check it connects OK
#Select Machine/Upload new firmware/Flashforge Creator I, II & X
#Chose the latest version (don't need the b versions which are for broken SD card machines)
#Hover over the upload button ready to press half a second after pressing the reset button (recessed next to the USB port on the back of the machine) 
#A green light should start flashing by the USB port on the machine. Takes about 30 seconds to upload and install the firmware
#Printer should reboot, check the version mtches what you were expecting and that replcatorg can connect and jog etc
#Use slic3r and gpx (see [[Calibrating a Replicator 1 dual with Sailfish firmware]])
Tutorial at https://www.raspberrypi-spy.co.uk/2012/07/stepper-motor-control-in-python/
28BYJ-48 specs: https://components101.com/motors/28byj-48-stepper-motor
ULN2003 specs: https://components101.com/stepper-motor-driver-ic-uln2003-pinout-datasheet
Code with corrected stepper sequence (as per one of the comments) is:
{{{
#!/usr/bin/python
# Import required libraries
import sys
import time
import RPi.GPIO as GPIO
 
# Use BCM GPIO references
# instead of physical pin numbers
GPIO.setmode(GPIO.BCM)
 
# Define GPIO signals to use
# Seem to be in order [Y,Pi,Bu,O]
# Physical pins 11,15,16,18
# GPIO17,GPIO22,GPIO23,GPIO24
StepPins = [17,22,23,24]
 
# Set all pins as output
# According to raspberry-gpio-python documentation (https://sourceforge.net/p/raspberry-gpio-python/wiki/Outputs/) 
# you can output to several channels at the same time by passing 2 list parameters instead of 2 integers.
for pin in StepPins:
  print "Setup pins"
  GPIO.setup(pin,GPIO.OUT)
  GPIO.output(pin, False) # the ULN2003 driver inverts the voltage, i.e. True is 0v

# Define advanced sequence
# as shown in manufacturer's datasheet (these are reversed, i.e. 1 = 0v) see above
# Sequence starts at step 2 (step 1 is Orange only at 0v)
Seq = [[1,0,0,1],
       [1,0,0,0],
       [1,1,0,0],
       [0,1,0,0],
       [0,1,1,0],
       [0,0,1,0],
       [0,0,1,1],
       [0,0,0,1]]
 
StepCount = len(Seq)
StepDir = 1 # Set to 1 or 2 for clockwise
            # Set to -1 or -2 for anti-clockwise
 
# Read wait time from command line
if len(sys.argv) > 1:
  WaitTime = int(sys.argv[1])/float(1000)
else:
  WaitTime = 10/float(1000)
 
# Initialise variables
StepCounter = 0
 
# Start main loop
while True:
 
  print StepCounter,
  print Seq[StepCounter]
 
  for pin in range(0, 4):
    xpin = StepPins[pin]#
    if Seq[StepCounter][pin]!=0:
      print " Enable GPIO %i" %(xpin)
      GPIO.output(xpin, True)
    else:
      GPIO.output(xpin, False)
 
  StepCounter += StepDir
 
  # If we reach the end of the sequence
  # start again
  if (StepCounter >= StepCount):
    StepCounter = 0
  if (StepCounter < 0):
    StepCounter = StepCount+StepDir
 
  # Wait before moving on
  time.sleep(WaitTime)
}}}
http://drupal.org/project/superfish has good installation instructions:

v1.8 installation

#Download the Superfish library https://github.com/mehrpadin/Superfish-for-Drupal (for v1.8 use the library v1.1) and extract it to sites/all/libraries/superfish (so that the superfish.js will be located at http://example.com/sites/all/libraries/superfish/superfish.js)
#Download and extract the Libraries module to sites/all/modules.
#Download and extract the Superfish module to /sites/all/modules.
chkAutoSave: true
txtUserName: Peter
txtBackupFolder: TWbackups
chkSaveBackups: true
{{{
	txtUserName: "Username for signing your edits",
	chkRegExpSearch: "Enable regular expressions for searches",
	chkCaseSensitiveSearch: "Case-sensitive searching",
	chkIncrementalSearch: "Incremental key-by-key searching",
	chkAnimate: "Enable animations",
	chkSaveBackups: "Keep backup file when saving changes",
	chkAutoSave: "Automatically save changes",
	chkGenerateAnRssFeed: "Generate an RSS feed when saving changes",
	chkSaveEmptyTemplate: "Generate an empty template when saving changes",
	chkOpenInNewWindow: "Open external links in a new window",
	chkToggleLinks: "Clicking on links to open tiddlers causes them to close",
	chkHttpReadOnly: "Hide editing features when viewed over HTTP",
	chkForceMinorUpdate: "Don't update modifier username and date when editing tiddlers",
	chkConfirmDelete: "Require confirmation before deleting tiddlers",
	chkInsertTabs: "Use the tab key to insert tab characters instead of moving between fields",
	txtBackupFolder: "Name of folder to use for backups",
	txtMaxEditRows: "Maximum number of rows in edit boxes",
	txtTheme: "Name of the theme to use",
	txtFileSystemCharSet: "Default character set for saving changes (Firefox/Mozilla only)"
}}}
| Format | Result | Markup |h
|Heading|!text|{{{|Heading|!text|}}}|
|>|colspan|{{{|>|colspan|}}}|
|vertical-align:top;rowspan*|left aligned text|{{{|vertical-align:top;rowspan*|left aligned text|}}}|
|~| right aligned|{{{|~| right aligned|}}}|
|~| centred |{{{|~| centred |}}}|
|Background|bgcolor(#DC1A1A):text|{{{|Background|bgcolor(#DC1A1A):text|}}}|
|Header|text|{{{|Header|text|h}}}|h
|Footer|text|{{{|Footer|text|f}}}|f
|Caption {{{|Caption|c}}}|c

"""*"""Default vertical alignment is centred
#Download debs from http://downloadarchive.documentfoundation.org/libreoffice/old
#Unpack
#Go to DEBS subdir
#Run {{{mkdir test; cd test; for i in ../*.deb; do dpkg-deb -x $i . ; done}}}
#Change where the user files will be stored by editing {{{UserInstallation=}}} in {{{opt/<version>/program/bootstraprc}}} - if you use {{{$ORIGIN}}} in the location it will be replaced by the {{{<version>/program}}} directory.
#Run the downloaded version by using {{{opt/<version>/program/oowriter}}} etc
#Sign in to Google
#Set up Navbar (menu/settings; home/recents; last app/app window; back/kill; arrows)
#Toggles: quick access on; add rotation; bluetooth
#Battery (text) and date (short day and month) in status bar
#Lock pattern
#Cerberus: sign in and check settings, esp number of attempts
#SMS backup: sign in
#Set ringtone and notification tone and volumes
#Volume locker enable
#Set alarm times and tones
#ADW launcher: recover from backup, set wallpaper
#Business Calendar: Widgets, calendars, bottom bar 180%, font sizes 120%, notifications on
#Switch off Google calendar notifications
#Switch off Gmail sounds
#Enable Usage timelines
#Sepect Hackers Keyboard
#Set up Clock sync
#Wuala sign in and set pin
#Gmail group share
#Gnotes link to gmail account
#Sign in business tasks 
The T420i fan is permanently on full. One solution is to install thinkfan. 
You need to have a file in /etc/modprobe.d with this line:
{{{
options thinkpad_acpi fan_control=1
}}}
Restart the system
Check it's running properly by trying {{{sudo thinkfan -n}}}

To start automatically on restart edit /etc/default/thinkfan

This is my fan speed table from /etc/thinkfan.conf 
It tries to keep the fan in 0 and 1 (off and inaudible) as much as possible. It ramps back down again much more quickly than the default settings.
{{{
#pbcs - Thinkpad T420s, fan states 0 (off) and 1 (1900rpm) are inaudible
#pbcs - state 2 (3400rpm) is quiet, state 3 (3700rpm) begins to be noticeable
#pbcs - This table tries to keep the fan in 0 and 1 as much as possible

(0,	0,	50)
(1,	48,	60)
(2,	55,	62)
(3,	58,	63)
(4,	59,	65)
(5,	60,	66)
(7,	63,	32767)
}}}
SUMMARY: don't bother, just put the SD card in your PC! Even with """DigiKam""".

Thunar should show connected PTP cameras automatically. If it doesn't make sure in Thunar that Edit/Preferences/Advanced/Volume Management is on, then click on the Configure link and check the Cameras tab has "Import digital photos when connected" ticked. It doesn't do what it says (import photos) but it does enable auto-connect for Thunar which should then show the camera in the left side-panel. Certainly with my camera (Panasonic Lumix """DMC-TZ40""" it's FAR too slow to be of any real use. E.g. 20 seconds of access time for each Thunar operation (even clicking on a menu item triggers a refresh) with 650 photos on the camera. Annoyingly Panasonic has decided that USB Disk mode will be read only so if you want to delete lots of photos you are stuck with agonisingly slow PTP mode.

REALLY the best bet is to take the memory card out and put it in your PC!

Useful debugging tools:
#install gphoto2 and use {{{gphoto2 --auto-detect}}} to make sure camera is visible to gphoto2
#use {{{udevadm monitor -p}}} to monitor udev when you attach the camera
Combine these three and you're sorted:
#Final Version (Mark Forster) successor to autofocus, superfocus, do it tomorrow (DIT) and Day Week Month (DWM) [[Link to instructions|http://archive.constantcontact.com/fs004/1100358239599/archive/1109980854493.html]] - best way to get through a to do list
#Pomodoro - spend (e.g.) 30 mins max before moving on
#Agile Results http://gettingresults.com/wiki/Getting_Started_with_Agile_Results - Rule of three things you want to achieve this year / month / week / day. Hotspots, vision, outcome, reflection pattern each week. Hotspots direct your energy to the area of life it's needed (e.g. work projects, personal projects, mind, body, emotions, career, financial, relationships, and fun)
"""FreeCAD""" for parametric model designs
Slic3r for slicing
use shell script in output stage to prepare the slic3r output for gpx

https://rigid.ink/blogs/news/138985991-specialist-filaments-why-you-d-use-them-and-best-printing-tips
Use the genmon plugin (install xfce4-genmon-plugin) to run a script like this:
{{{
#!/bin/bash
# Get the top $1 processes (and their percentages) from top
# and print $2 characters of info

NUM=${1:-1} # Number of lines to print, default 1
LEN=${2:-10} # Length of lines in characters, default 10

TASK_COMM_LEN=16 # This is a kernel build parameter which sets the max len of the command in /proc/pid/comm

if [ "$LEN" -gt $((TASK_COMM_LEN + 4)) ]; then LEN=$((TASK_COMM_LEN + 4)); fi
PADDING="                              "

top -bn2 -w512 -d0.5 | # run top in batch mode for 2 iterations (1 doesn't give good results)
  sed -n "n;n;n;n;n;n;n; :part1 n; s/%CPU//; T part1; :part2 n;p; b part2" | # discard until second header (8 n's to skip first header)
  grep -v "top$" | # don't include the entry for top
  grep -v "nethogs" | # of nethogs (run as a monitor for network usage)
  head -n $NUM | # get the required number of lines
  while read  PID USER PR NI VIRT RES SHR S CPU MEM TIME COMMAND; do # extract fields
    if [ "$CPU" != "0.0" ]; then
      # round decimals in CPU and pad line with spaces
      UNITS=${CPU%.*}
      DECS=${CPU##*.}
      if [ $DECS -gt 4 ]; then UNITS=$((UNITS + 1)); fi
      echo "$UNITS $COMMAND $PADDING"
    else
      # indicate zero CPU lines - empty output breaks pipe
     echo "--$PADDING"
    fi
  done |
  cut -c 1-$LEN # trim to size
}}}
Set the properties for the Generic Monitor to the font (best to use a mono font, e.g. DejaVu Sans Mono), and use parameters to the script to set the number of processes to display and the line length. I use 2 and 15 with font size 7.
To get this working properly on xubuntu I had to:
*Disable the psmouse module:
{{{
$ cat /etc/modprobe.d/pbcs-blacklist-psmouse.conf 
# on Dell 7350 psmouse generates duplicate devices,
# correct one is hid_multitouch for touchpad
blacklist psmouse
}}}
*The touchpad stops working after removing and re-attaching the keyboard base. To re-enable it unload and reload the kernel module hid_multitouch.
{{{
$ cat restart-touchpad.sh
#!/bin/bash
sudo modprobe -r hid_multitouch
sudo modprobe hid_multitouch
}}}
*You can have a desktop file to launch the script manually or...
*Add in a udev rule to call that script when the keyboard unit is removed and re-inserted. I used detection of the USB hub as the trigger. See [[udev udevadm auto run on plugin device]]
This minimal chromium-browser kiosk uses only 3GB on disk so can easily be run from a USB stick
Running on: Acer Revo with NVIDIA Ion graphics connected to """HT231HPB""" 23 inch touchscreen
It includes a python button bar which floats on top of the browser window to provide easy access functions for home, internal links etc.
*Install ubuntu server with no extras
*Boot into the server
*{{{apt install --no-install-recommends xserver-xorg x11-xserver-utils xinit chromium-browser unclutter xdotool}}}
*To get wifi working:
**{{{apt install --no-install-recommends wpasupplicant}}}
**{{{sudo nano /etc/netplan/02-wifi.yaml}}}
{{{
network:
  version: 2
  wifis:
    wlp5s0:
      dhcp4: true
      dchp6: false
      optional: true
      access-points:
        "Access-point-name":
          password: "passwordtext"
}}}
**{{{sudo netplan apply}}}
**{{{ifconfig}}} - check interface is up and associated. Look in dmesg or /var/log/syslog for clues for failures.
**Add {{{optional: true}}} to any network devices that may not be connected otherwise the boot will wait for ages with "A start job is running for wait for network to be configured"
*Python floating button bar:
**Install the python modules {{{apt install --no-install-recommends  python3-pip python3-pil-imagetk}}}
**Use pip to install the simple GUI module {{{pip3 install pysimplegui}}}
**Here is the program:
{{{
import PySimpleGUI as sg
import subprocess

def ButtonBar():
  """Put up a button bar above the browser"""

  # Start the browser (see below for start-chromium script)
  subprocess.run(['/home/kiosk/start-chromium', '/home/kiosk/gohome.html'])

  # Options for the button bar
  sg.SetOptions(
    window_location=(1000,960),
    border_width=3, # bevel around buttons
    background_color='white',
    element_padding=(1,1),
    margins=(0,0), # background visible around edges of button bar
    button_element_size=(1,1),
    auto_size_buttons=True)

  layout =  [[ sg.Button('', key='home', image_filename='home.png'),
               sg.Button('', key='messy', image_filename='messy.png'),
               sg.Button('', key='exit', image_filename='exit.png')
            ]]

  # Put up the button bar
  window = sg.Window('ButtonBar', no_titlebar=True, keep_on_top=True).Layout(layout)

  # --- Loop taking in user input (events) --- #
  while True:
    (event, value) = window.Read(10)
    if event == 'exit':
        break # exit button clicked
    elif event == 'home':
        subprocess.run(['chromium-browser', 'https://lymingtonchurch.org'])
    elif event == 'messy':
        subprocess.run(['chromium-browser', 'https://lymingtonchurch.org/messy-church'])

if __name__ == '__main__':
  ButtonBar()
}}}
**The start-chromium script starts chromium as a background process with all the kiosk switches. The ignore-gpu-blacklist is required for nouveau to work with chromium:
{{{
$ cat start-chromium 
#!/bin/bash
# Start Chromium in kiosk mode
nohup chromium-browser \
  --kiosk --incognito \
  --ignore-gpu-blacklist \
  --start-fullscreen --window-size=1920,1080 --window-position=0,0 \
  $1 &
}}}
*Now the final piece of the jigsaw, the xinitrc file to define what startx will do: {{{nano .xinitrc}}}
{{{
#!/bin/bash
# Switch off display timeouts
xset s off
xset -dpms
xset s noblank

# Auto hide cursor after 2 seconds of inactivity. 2 seconds is enough to see where you touched the screen
unclutter -idle 2 &

# Start the button bar (also starts the browser)
python3 python-bar.py &

# Keep ButtonBar on top; quit if the button bar (python3) is closed
while pgrep python3; do
  sleep 2
  xdotool search --name ButtonBar windowraise
done
}}}
*And away we go! {{{startx}}}
*To make the kiosk start automatically on login, put this at the end of {{{~/.bashrc}}}
{{{
echo "Starting kiosk..."
sleep 3 # time to ^C if required
exec startx
}}}
*To log user kiosk in automatically, put this in {{{/etc/systemd/system/getty@tty1.service.d/override.conf}}}
{{{
[Service]
ExecStart=
ExecStart=-/sbin/getty --autologin kiosk --noclear %I
}}}
f3probe, part of the f3 package will check an SD card and tell you its true capacity quickly.
For example:
{{{
~$ sudo f3probe --reset-type=2 /dev/mmcblk0
F3 probe 7.0
Copyright (C) 2010 Digirati Internet LTDA.
This is free software; see the source for copying conditions.

WARNING: Probing normally takes from a few seconds to 15 minutes, but
         it can take longer. Please be patient.

Probe finished, recovering blocks... Done

Bad news: The device `/dev/mmcblk0' is a counterfeit of type limbo

You can "fix" this device using the following command:
f3fix --last-sec=8389644 /dev/mmcblk0

Device geometry:
	         *Usable* size: 4.00 GB (8389645 blocks)
	        Announced size: 31.25 GB (65536000 blocks)
	                Module: 32.00 GB (2^35 Bytes)
	Approximate cache size: 127.00 MB (260096 blocks), need-reset=no
	   Physical block size: 512.00 Byte (2^9 Bytes)

Probe time: 6'29"

~$ sudo f3fix --last-sec=8389644 /dev/mmcblk0
F3 fix 7.0
Copyright (C) 2010 Digirati Internet LTDA.
This is free software; see the source for copying conditions.

Error: Partition(s) 2 on /dev/mmcblk0 have been written, but we have been unable to inform the kernel of the change, probably because it/they are in use.  As a result, the old partition(s) will remain in use.  You should reboot now before making further changes.
Drive `/dev/mmcblk0' was successfully fixed
}}}
/***
Description: Contains the stuff you need to use Tiddlyspot
Note, you also need UploadPlugin, PasswordOptionPlugin and LoadRemoteFileThroughProxy
from http://tiddlywiki.bidix.info for a complete working Tiddlyspot site.
***/
//{{{

// edit this if you are migrating sites or retrofitting an existing TW
config.tiddlyspotSiteId = 'peterthevicar';

// make it so you can by default see edit controls via http
config.options.chkHttpReadOnly = false;
window.readOnly = false; // make sure of it (for tw 2.2)
window.showBackstage = true; // show backstage too

// disable autosave in d3
if (window.location.protocol != "file:")
	config.options.chkGTDLazyAutoSave = false;

// tweak shadow tiddlers to add upload button, password entry box etc
with (config.shadowTiddlers) {
	SiteUrl = 'http://'+config.tiddlyspotSiteId+'.tiddlyspot.com';
	SideBarOptions = SideBarOptions.replace(/(<<saveChanges>>)/,"$1<<tiddler TspotSidebar>>");
	OptionsPanel = OptionsPanel.replace(/^/,"<<tiddler TspotOptions>>");
	DefaultTiddlers = DefaultTiddlers.replace(/^/,"[[WelcomeToTiddlyspot]] ");
	MainMenu = MainMenu.replace(/^/,"[[WelcomeToTiddlyspot]] ");
}

// create some shadow tiddler content
merge(config.shadowTiddlers,{

'TspotControls':[
 "| tiddlyspot password:|<<option pasUploadPassword>>|",
 "| site management:|<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . .  " + config.tiddlyspotSiteId + ">>//(requires tiddlyspot password)//<br>[[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]], [[download (go offline)|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download]]|",
 "| links:|[[tiddlyspot.com|http://tiddlyspot.com/]], [[FAQs|http://faq.tiddlyspot.com/]], [[blog|http://tiddlyspot.blogspot.com/]], email [[support|mailto:support@tiddlyspot.com]] & [[feedback|mailto:feedback@tiddlyspot.com]], [[donate|http://tiddlyspot.com/?page=donate]]|"
].join("\n"),

'TspotOptions':[
 "tiddlyspot password:",
 "<<option pasUploadPassword>>",
 ""
].join("\n"),

'TspotSidebar':[
 "<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . .  " + config.tiddlyspotSiteId + ">><html><a href='http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download' class='button'>download</a></html>"
].join("\n"),

'WelcomeToTiddlyspot':[
 "This document is a ~TiddlyWiki from tiddlyspot.com.  A ~TiddlyWiki is an electronic notebook that is great for managing todo lists, personal information, and all sorts of things.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //What now?// &nbsp;&nbsp;@@ Before you can save any changes, you need to enter your password in the form below.  Then configure privacy and other site settings at your [[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]] (your control panel username is //" + config.tiddlyspotSiteId + "//).",
 "<<tiddler TspotControls>>",
 "See also GettingStarted.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Working online// &nbsp;&nbsp;@@ You can edit this ~TiddlyWiki right now, and save your changes using the \"save to web\" button in the column on the right.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Working offline// &nbsp;&nbsp;@@ A fully functioning copy of this ~TiddlyWiki can be saved onto your hard drive or USB stick.  You can make changes and save them locally without being connected to the Internet.  When you're ready to sync up again, just click \"upload\" and your ~TiddlyWiki will be saved back to tiddlyspot.com.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Help!// &nbsp;&nbsp;@@ Find out more about ~TiddlyWiki at [[TiddlyWiki.com|http://tiddlywiki.com]].  Also visit [[TiddlyWiki.org|http://tiddlywiki.org]] for documentation on learning and using ~TiddlyWiki. New users are especially welcome on the [[TiddlyWiki mailing list|http://groups.google.com/group/TiddlyWiki]], which is an excellent place to ask questions and get help.  If you have a tiddlyspot related problem email [[tiddlyspot support|mailto:support@tiddlyspot.com]].",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Enjoy :)// &nbsp;&nbsp;@@ We hope you like using your tiddlyspot.com site.  Please email [[feedback@tiddlyspot.com|mailto:feedback@tiddlyspot.com]] with any comments or suggestions."
].join("\n")

});
//}}}
"""LibreOffice""" 5 has now dropped support for Type 1 fonts (*.pfb) so you need to convert them to otf using fontforge:
*Install fontforge
*write a script to use fontforge to convert pfb files (based on one at https://fontforge.github.io/scripting-tutorial.html)
{{{
$ cat /usr/local/bin/fontconvert 
#!/usr/bin/fontforge -lang=ff
i=1
format=".otf"
while ( i < $argc )
  if ( $argv[i]=="-format" || $argv[i]=="--format" )
    i=i+1
    if ( i<$argc )
      format = $argv[i]
      if ( format!=".ttf" && format!=".otf" && \
	  format!=".pfb" && format!=".svg" )
	Error( "Expected one of '.ttf', '.otf', '.pfb' or '.svg' for format" )
      endif
    endif
  else
    Open($argv[i])
    Print("Converting "+$fontname)
    Generate($argv[i]:r + $fontname + format)
  endif
  i = i+1
endloop
}}}
*execute the script, e.g.
{{{
$ cd .fonts
$ fontconvert *.pfb
Copyright (c) 2000-2014 by George Williams. See AUTHORS for Contributors.
 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
 with many parts BSD <http://fontforge.org/license.html>. Please read LICENSE.
 Based on sources from 20170106-ML-D.
 Based on source from git with hash: 
Converting URWGothicL-Book
Converting URWGothicL-Demi
Converting URWGothicL-BookObli
Converting URWGothicL-DemiObli
Converting URWBookmanL-Ligh
Converting URWBookmanL-DemiBold
Converting URWBookmanL-LighItal
Converting URWBookmanL-DemiBoldItal
Converting CenturySchL-Roma
Converting Dingbats
Converting NimbusSanL-Regu
Converting NimbusSanL-Bold
Converting NimbusSanL-ReguItal
Converting NimbusSanL-BoldItal
Converting NimbusSanL-ReguCond
Converting NimbusSanL-BoldCond
Converting NimbusSanL-ReguCondItal
Converting NimbusSanL-BoldCondItal
Converting StandardSymL
Converting Symbol
Converting URWChanceryL-MediItal
}}}
systemd (v225) is doing strange things with USB disk mounts and failing with a message "Unit is bound to inactive unit" resulting in the mount being followed immediately by an unmount.
{{{
$ systemctl status Disks-Scratch.mount
● Disks-Scratch.mount - /Disks/Scratch
   Loaded: loaded (/etc/fstab)
   Active: inactive (dead) since Mon 2016-03-21 13:21:39 GMT; 5min ago
    Where: /Disks/Scratch
     What: /dev/disk/by-label/Scratch
     Docs: man:fstab(5)
           man:systemd-fstab-generator(8)

Mar 21 13:21:39 Neelix systemd[1]: Disks-Scratch.mount: Unit is bound to inactive unit dev-disk...too.
Mar 21 13:21:39 Neelix systemd[1]: Unmounting /Disks/Scratch...
Mar 21 13:21:39 Neelix systemd[1]: Unmounted /Disks/Scratch.
Hint: Some lines were ellipsized, use -l to show in full.
}}}
This SEEMS to be linked to device id's changing between suspend and resume which upsets systemd and it then can't remount manually.
See [[Doing something after a resume from suspend]] to see how to re-mount automatically following resume.
To do it manually, disable the mount unit before re-mounting:
{{{
$ systemctl disable Disks-Scratch.mount
$ sudo mount /Disks/Scratch
$ systemctl status Disks-Scratch.mount
● Disks-Scratch.mount - /Disks/Scratch
   Loaded: loaded (/etc/fstab)
   Active: active (mounted) since Mon 2016-03-21 13:27:41 GMT; 1s ago
    Where: /Disks/Scratch
     What: /dev/sdb5
     Docs: man:fstab(5)
           man:systemd-fstab-generator(8)
}}}
http://www.techknow.t0xic.nl/forum/index.php?board=9.0

*Download Uberoid to HDD on a Linux PC from http://www.techknow.t0xic.nl/forum/index.php?board=9.0
*Extract archive to HDD
*Check {{{Universal_Uberoid_WM8650_v12_Tablet_Compatibility_List.txt}}} for model number (mine is 41 = """8inch_vt1603, uzImageGREEN.bin, wmt_scriptcmd8, RT3070""")
{{{
cd WM8650_Universal_Uberoid_v12.1_22SEP2012_TekNotes
sudo ln -s $(pwd) /sdcard
mv /sdcard/Changer_files/ /sdcard/changer_files # case error in CHANGER script
bash /sdcard/CHANGER_NIX.sh
}}}
*Select RT then 41
----
*Remove tablet SD card and mount on the PC
*Backup Firmwareinstall directory just in case
*Copy {{{FirmwareInstall}}} directory and {{{wmt_scriptcmd}}} onto SD
*Put SD card pack into tablet
*On booting, the upgrade commences
*After upgrade remove SD card and tablet will reboot
*Once up and running, re-insert SD card and remove {{{wmt_scriptcmd}}} or it will re-do the upgrade when you next reboot.
----
*After booting if battery sticks at 100% install Terminal emulator and do:
{{{
$ su
# wmtenv set wmt.vt160x.bat 0:1:10:0
# reboot
}}}
(see the FAQ's on the ROM post)
R=LuCi interface to router; C=Computer
#R: Status/Overview, check current version and double check make and model of router
#C: Download the .bin file for the new version
#C: Check the MD5 checksum for the .bin file
#R: System/Backup Flash
#R: Backup/Generate Archive (save to Computer)
#R: Flash new firmware, check Keep Settings, browse to .bin saved earlier
#R: Hit the flash image button
#R: Check the MD5 checksum calculated earlier
#R: Wait a while (2-3 minutes?) and the router should comeback to the login page. Password will be the same as before.
#R: System/Status check the new version is installed
#R: System/Backup/Generate archive and save to C
Instructions on disk:
Workflow:

FIRST: Do an auto channel rescan on the enigma, then a manual scan of channels 55 and 56

1) Get enigma2 dir from enigma box

$ rcp -r root@192.168.1.20:/etc/enigma2 .

Go into downloaded directory

$ cd enigma2

Open up the README.txt file

2) Update bouquets-def.csv (or create a new one if none found)

$ bash ./bouquets-maker.sh

This will create a vanilla bouquets-def.csv in new/ which you move to
the current directory to keep it from being overwritten in the next run.
Note particularly if there are new channels. They will be at the foot of
the new csv file in bouquet z unless you change this.

3) Edit the bouquets-def.csv file (e.g. with LibreOffice Calc) to your liking

(The structure of the csv file is documented in bouquets-maker.sh)

a) Define all the bouquets that you want

b) In column H (field 8) of the channel records put a few sort overrides, 
for example I put 1,2,3,4 for BBC1, BBC2, ITV, Channel 4
You may want to put in at least as many as you can see at once in your enigma2 skin (13 for me)
Put 100 in all the others to make sure they sort below the ones you've marked up

c) Sort the channel records in the csv file as you want them sorted
e.g. sort by column H then F (sort override, name)

d) Save the csv file, preserving its format as a csv with colon-separated fields (tick 'Edit filter settings' in the save dialog to set this)

4) Run bouquets-maker.sh again to produce the bouquets

Save the README.txt file if you've edited it as the next step copies it

$ bash ./bouquets-maker.sh

This will create a directory ./new with all the bouquets and copies of
 the csv file, scripts and README.txt
 
There shouldn't be any NEW channels this time.

5) Upload the files to the enigma (make sure to use binary mode or it will add crlf line endings)

bash ./bouquets-transfer.sh 192.168.1.20 root <password>

6) Poke the enigma to get it to load the new bouquets

bash ./bouquets-reload.sh 192.168.1.20 root <password>
Clock not set correctly?
{{{exiftool -alldates+=0:51 *.jpg}}} - adds 51 minutes to all date stamps

Set file and exif modification dates to the creation date in the EXIF data:
{{{exiftool "-File:FileModifyDate<Exif:CreateDate" "-Exif:ModifyDate<Exif:CreateDate" dir-of-images}}}

Set file names to the photo creation date in their exif tag:
{{{exiftool -recurse -extension jpg -extension jpeg -ignoreMinorErrors '-FileName<CreateDate' -d %Y-%m%d-%H%M%S.jpg dir-of-images}}}

Script for clearing out the _original files after an exiftool operation:
{{{for d in *; do if [ -d "$d" ]; then mv "$d"/*_original /Disks/Scratch/temp/photo-originals/"$d"; fi; done}}}

Find all exiftool groups {{{exiftool -listg}}}
Find all exiftool tags for a given group, use -s to get tag name instead of description, e.g. {{{exiftool -s '-File:all' x.jpg}}}
To check current Drupal version, PHP version, memory limit etc, go to admin/reports/status. Top line is drupal version.

Upgrade has two stages: test and deploy
!!!Test
#download new version from drupal.org, read release notes on download page
#(cpanel) upload and unpack to a subdir (e.g. drupal-new.ver)
#(cpanel) pack the sites dir of the current version
#(cpanel) unpack to replace the new version's sites dir
#(For live update put current site into maintenance mode)
#Copy the database to a new database, eg drupal-new-ver
##(phpMyAdmin) copy prefix_drupal-curr-ver to prefix_drupal-new-ver
##("""MySQL""" Databases) add privileged user to new database (near foot of dialog) and delete any previous databases no longer being used
#Edit the sites/default/settings.php to point to drupal-new-ver database and set the new $base_url to www.mysite.com/new.ver
##(cpanel) add write permission for sites/default dir and for settings.php
##(cpanel) code edit settings.php and change database name
##(NOT needed for live update) (cpanel) add subdir to base_url
##(cpanel) save changes
##(cpanel) revert the permissions
#Test:
##(For live update go to www.mysite.com) go to www.mysite.com/new.ver
##(NOT needed for live update) log in as webmaster
##(NOT needed for live update) go to admin/configuration to enable maintenance mode
##go to www.mysite.com/new.ver/update.php to perform any database updates
##go to admin/modules and perform any automatic module updates pending in the update tab
##perform any database updates required
##make sure tmp file directory is correct (for subdirectory running it needs to be ../../tmp)
##exit maintenance mode
##check the status report
##generally look around and test things
##go back into maintenance mode when it's all checked out

!!!Deploy - swap to new system
(This will lose any changes made to the live site while testing the new version)
#(NOT needed for live update) Reset base_url to top level of website
##(cpanel) add write permission for sites/default dir and for settings.php
##(cpanel) code edit settings.php and change base_url to remove subdir, save changes
##(cpanel) revert the permissions
#(Already done for live update) Put old site into maintenance mode
##Log in to mysite.com as webmaster
##go to admin/configuration and put into maintenance mode
#Redirect accesses to new site
##(cpanel) *** switch to new subdir by changing the top level htaccess rewrite rules (4 changes) ***
##clear caches (config/development/performance/clear all caches)
##check status report for any errors
##check all is well and then exit maintenance mode
#REMOVE old version if it had security flaws
#(OR) Make the previous version functional if required
##(cpanel)change the old version's sites/default/settings base url to include the subdirectory
##go to mysite.com/old.ver/user/login and login as administrator to take out of maintenance mode
#(myadmin)Export previous live database to backup on PC
#(cpanel)Pack sites dir as backup on server and download to PC
*Download the new firmware
*Save old config and flash new firmware, keeping old config
*Re-open """LuCI"""
*System/Software
**update
***if if won't update, it may be the dnsmasq process didn't start properly:
***ssh from connected PC (e.g. {{{ssh root@192.168.1.4}}})
***{{{nslookup bbc.co.uk}}} - if this fails try restarting dnsmasq. also can try {{{nslookup bbc.co.uk 192.168.1.1}}} to make sure DNS is available there
***{{{/etc/init.d/dnsmasq restart}}}
**install: block-mount, kmod-usb-storage, nfs-kernel-server
**Reboot the """WRT1200""" to get the System/Mount Points menu into """LuCI"""
**Check the mount points are all there and working
**Check the {{{/etc/exports}}} is correct (also see [[NFS with enigma2 OpenVIX and ubuntu]])
**Restart the Enigma box and anything else connected to the NFS shares
| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |
| 28/06/2020 19:18:01 | Peter | [[/|http://peterthevicar.tiddlyspot.com/]] | [[store.cgi|http://peterthevicar.tiddlyspot.com/store.cgi]] | . | [[index.html | http://peterthevicar.tiddlyspot.com/index.html]] | . |
| 18/07/2020 21:53:11 | Peter | [[/|http://peterthevicar.tiddlyspot.com/]] | [[store.cgi|http://peterthevicar.tiddlyspot.com/store.cgi]] | . | [[index.html | http://peterthevicar.tiddlyspot.com/index.html]] | . | ok |
| 18/07/2020 23:41:40 | Peter | [[/|http://peterthevicar.tiddlyspot.com/]] | [[store.cgi|http://peterthevicar.tiddlyspot.com/store.cgi]] | . | [[index.html | http://peterthevicar.tiddlyspot.com/index.html]] | . | ok |
| 18/07/2020 23:42:09 | Peter | [[/|http://peterthevicar.tiddlyspot.com/]] | [[store.cgi|http://peterthevicar.tiddlyspot.com/store.cgi]] | . | [[index.html | http://peterthevicar.tiddlyspot.com/index.html]] | . | ok |
| 19/07/2020 00:06:19 | Peter | [[/|http://peterthevicar.tiddlyspot.com/]] | [[store.cgi|http://peterthevicar.tiddlyspot.com/store.cgi]] | . | [[index.html | http://peterthevicar.tiddlyspot.com/index.html]] | . | ok |
| 19/07/2020 00:46:29 | Peter | [[/|http://peterthevicar.tiddlyspot.com/]] | [[store.cgi|http://peterthevicar.tiddlyspot.com/store.cgi]] | . | [[index.html | http://peterthevicar.tiddlyspot.com/index.html]] | . |
| 19/07/2020 12:22:58 | Peter | [[/|http://peterthevicar.tiddlyspot.com/]] | [[store.cgi|http://peterthevicar.tiddlyspot.com/store.cgi]] | . | [[index.html | http://peterthevicar.tiddlyspot.com/index.html]] | . | failed |
| 19/07/2020 12:23:11 | Peter | [[/|http://peterthevicar.tiddlyspot.com/]] | [[store.cgi|http://peterthevicar.tiddlyspot.com/store.cgi]] | . | [[index.html | http://peterthevicar.tiddlyspot.com/index.html]] | . |
| 19/07/2020 23:43:10 | Peter | [[/|http://peterthevicar.tiddlyspot.com/]] | [[store.cgi|http://peterthevicar.tiddlyspot.com/store.cgi]] | . | [[index.html | http://peterthevicar.tiddlyspot.com/index.html]] | . |
| 03/04/2021 22:22:49 | Peter | [[/|http://peterthevicar.tiddlyspot.com/#Archive]] | [[store.cgi|http://peterthevicar.tiddlyspot.com/store.cgi]] | . | [[index.html | http://peterthevicar.tiddlyspot.com/index.html]] | . |
/***
|''Name:''|UploadPlugin|
|''Description:''|Save to web a TiddlyWiki|
|''Version:''|4.1.3|
|''Date:''|Feb 24, 2008|
|''Source:''|http://tiddlywiki.bidix.info/#UploadPlugin|
|''Documentation:''|http://tiddlywiki.bidix.info/#UploadPluginDoc|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
|''Requires:''|PasswordOptionPlugin|
***/
//{{{
version.extensions.UploadPlugin = {
	major: 4, minor: 1, revision: 3,
	date: new Date("Feb 24, 2008"),
	source: 'http://tiddlywiki.bidix.info/#UploadPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	coreVersion: '2.2.0'
};

//
// Environment
//

if (!window.bidix) window.bidix = {}; // bidix namespace
bidix.debugMode = false;	// true to activate both in Plugin and UploadService
	
//
// Upload Macro
//

config.macros.upload = {
// default values
	defaultBackupDir: '',	//no backup
	defaultStoreScript: "store.php",
	defaultToFilename: "index.html",
	defaultUploadDir: ".",
	authenticateUser: true	// UploadService Authenticate User
};
	
config.macros.upload.label = {
	promptOption: "Save and Upload this TiddlyWiki with UploadOptions",
	promptParamMacro: "Save and Upload this TiddlyWiki in %0",
	saveLabel: "save to web", 
	saveToDisk: "save to disk",
	uploadLabel: "upload"	
};

config.macros.upload.messages = {
	noStoreUrl: "No store URL in parmeters or options",
	usernameOrPasswordMissing: "Username or password missing"
};

config.macros.upload.handler = function(place,macroName,params) {
	if (readOnly)
		return;
	var label;
	if (document.location.toString().substr(0,4) == "http") 
		label = this.label.saveLabel;
	else
		label = this.label.uploadLabel;
	var prompt;
	if (params[0]) {
		prompt = this.label.promptParamMacro.toString().format([this.destFile(params[0], 
			(params[1] ? params[1]:bidix.basename(window.location.toString())), params[3])]);
	} else {
		prompt = this.label.promptOption;
	}
	createTiddlyButton(place, label, prompt, function() {config.macros.upload.action(params);}, null, null, this.accessKey);
};

config.macros.upload.action = function(params)
{
		// for missing macro parameter set value from options
		if (!params) params = {};
		var storeUrl = params[0] ? params[0] : config.options.txtUploadStoreUrl;
		var toFilename = params[1] ? params[1] : config.options.txtUploadFilename;
		var backupDir = params[2] ? params[2] : config.options.txtUploadBackupDir;
		var uploadDir = params[3] ? params[3] : config.options.txtUploadDir;
		var username = params[4] ? params[4] : config.options.txtUploadUserName;
		var password = config.options.pasUploadPassword; // for security reason no password as macro parameter	
		// for still missing parameter set default value
		if ((!storeUrl) && (document.location.toString().substr(0,4) == "http")) 
			storeUrl = bidix.dirname(document.location.toString())+'/'+config.macros.upload.defaultStoreScript;
		if (storeUrl.substr(0,4) != "http")
			storeUrl = bidix.dirname(document.location.toString()) +'/'+ storeUrl;
		if (!toFilename)
			toFilename = bidix.basename(window.location.toString());
		if (!toFilename)
			toFilename = config.macros.upload.defaultToFilename;
		if (!uploadDir)
			uploadDir = config.macros.upload.defaultUploadDir;
		if (!backupDir)
			backupDir = config.macros.upload.defaultBackupDir;
		// report error if still missing
		if (!storeUrl) {
			alert(config.macros.upload.messages.noStoreUrl);
			clearMessage();
			return false;
		}
		if (config.macros.upload.authenticateUser && (!username || !password)) {
			alert(config.macros.upload.messages.usernameOrPasswordMissing);
			clearMessage();
			return false;
		}
		bidix.upload.uploadChanges(false,null,storeUrl, toFilename, uploadDir, backupDir, username, password); 
		return false; 
};

config.macros.upload.destFile = function(storeUrl, toFilename, uploadDir) 
{
	if (!storeUrl)
		return null;
		var dest = bidix.dirname(storeUrl);
		if (uploadDir && uploadDir != '.')
			dest = dest + '/' + uploadDir;
		dest = dest + '/' + toFilename;
	return dest;
};

//
// uploadOptions Macro
//

config.macros.uploadOptions = {
	handler: function(place,macroName,params) {
		var wizard = new Wizard();
		wizard.createWizard(place,this.wizardTitle);
		wizard.addStep(this.step1Title,this.step1Html);
		var markList = wizard.getElement("markList");
		var listWrapper = document.createElement("div");
		markList.parentNode.insertBefore(listWrapper,markList);
		wizard.setValue("listWrapper",listWrapper);
		this.refreshOptions(listWrapper,false);
		var uploadCaption;
		if (document.location.toString().substr(0,4) == "http") 
			uploadCaption = config.macros.upload.label.saveLabel;
		else
			uploadCaption = config.macros.upload.label.uploadLabel;
		
		wizard.setButtons([
				{caption: uploadCaption, tooltip: config.macros.upload.label.promptOption, 
					onClick: config.macros.upload.action},
				{caption: this.cancelButton, tooltip: this.cancelButtonPrompt, onClick: this.onCancel}
				
			]);
	},
	options: [
		"txtUploadUserName",
		"pasUploadPassword",
		"txtUploadStoreUrl",
		"txtUploadDir",
		"txtUploadFilename",
		"txtUploadBackupDir",
		"chkUploadLog",
		"txtUploadLogMaxLine"		
	],
	refreshOptions: function(listWrapper) {
		var opts = [];
		for(i=0; i<this.options.length; i++) {
			var opt = {};
			opts.push();
			opt.option = "";
			n = this.options[i];
			opt.name = n;
			opt.lowlight = !config.optionsDesc[n];
			opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
			opts.push(opt);
		}
		var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
		for(n=0; n<opts.length; n++) {
			var type = opts[n].name.substr(0,3);
			var h = config.macros.option.types[type];
			if (h && h.create) {
				h.create(opts[n].colElements['option'],type,opts[n].name,opts[n].name,"no");
			}
		}
		
	},
	onCancel: function(e)
	{
		backstage.switchTab(null);
		return false;
	},
	
	wizardTitle: "Upload with options",
	step1Title: "These options are saved in cookies in your browser",
	step1Html: "<input type='hidden' name='markList'></input><br>",
	cancelButton: "Cancel",
	cancelButtonPrompt: "Cancel prompt",
	listViewTemplate: {
		columns: [
			{name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
			{name: 'Option', field: 'option', title: "Option", type: 'String'},
			{name: 'Name', field: 'name', title: "Name", type: 'String'}
			],
		rowClasses: [
			{className: 'lowlight', field: 'lowlight'} 
			]}
};

//
// upload functions
//

if (!bidix.upload) bidix.upload = {};

if (!bidix.upload.messages) bidix.upload.messages = {
	//from saving
	invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
	backupSaved: "Backup saved",
	backupFailed: "Failed to upload backup file",
	rssSaved: "RSS feed uploaded",
	rssFailed: "Failed to upload RSS feed file",
	emptySaved: "Empty template uploaded",
	emptyFailed: "Failed to upload empty template file",
	mainSaved: "Main TiddlyWiki file uploaded",
	mainFailed: "Failed to upload main TiddlyWiki file. Your changes have not been saved",
	//specific upload
	loadOriginalHttpPostError: "Can't get original file",
	aboutToSaveOnHttpPost: 'About to upload on %0 ...',
	storePhpNotFound: "The store script '%0' was not found."
};

bidix.upload.uploadChanges = function(onlyIfDirty,tiddlers,storeUrl,toFilename,uploadDir,backupDir,username,password)
{
	var callback = function(status,uploadParams,original,url,xhr) {
		if (!status) {
			displayMessage(bidix.upload.messages.loadOriginalHttpPostError);
			return;
		}
		if (bidix.debugMode) 
			alert(original.substr(0,500)+"\n...");
		// Locate the storeArea div's 
		var posDiv = locateStoreArea(original);
		if((posDiv[0] == -1) || (posDiv[1] == -1)) {
			alert(config.messages.invalidFileError.format([localPath]));
			return;
		}
		bidix.upload.uploadRss(uploadParams,original,posDiv);
	};
	
	if(onlyIfDirty && !store.isDirty())
		return;
	clearMessage();
	// save on localdisk ?
	if (document.location.toString().substr(0,4) == "file") {
		var path = document.location.toString();
		var localPath = getLocalPath(path);
		saveChanges();
	}
	// get original
	var uploadParams = new Array(storeUrl,toFilename,uploadDir,backupDir,username,password);
	var originalPath = document.location.toString();
	// If url is a directory : add index.html
	if (originalPath.charAt(originalPath.length-1) == "/")
		originalPath = originalPath + "index.html";
	var dest = config.macros.upload.destFile(storeUrl,toFilename,uploadDir);
	var log = new bidix.UploadLog();
	log.startUpload(storeUrl, dest, uploadDir,  backupDir);
	displayMessage(bidix.upload.messages.aboutToSaveOnHttpPost.format([dest]));
	if (bidix.debugMode) 
		alert("about to execute Http - GET on "+originalPath);
	var r = doHttp("GET",originalPath,null,null,username,password,callback,uploadParams,null);
	if (typeof r == "string")
		displayMessage(r);
	return r;
};

bidix.upload.uploadRss = function(uploadParams,original,posDiv) 
{
	var callback = function(status,params,responseText,url,xhr) {
		if(status) {
			var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
			displayMessage(bidix.upload.messages.rssSaved,bidix.dirname(url)+'/'+destfile);
			bidix.upload.uploadMain(params[0],params[1],params[2]);
		} else {
			displayMessage(bidix.upload.messages.rssFailed);			
		}
	};
	// do uploadRss
	if(config.options.chkGenerateAnRssFeed) {
		var rssPath = uploadParams[1].substr(0,uploadParams[1].lastIndexOf(".")) + ".xml";
		var rssUploadParams = new Array(uploadParams[0],rssPath,uploadParams[2],'',uploadParams[4],uploadParams[5]);
		var rssString = generateRss();
		// no UnicodeToUTF8 conversion needed when location is "file" !!!
		if (document.location.toString().substr(0,4) != "file")
			rssString = convertUnicodeToUTF8(rssString);	
		bidix.upload.httpUpload(rssUploadParams,rssString,callback,Array(uploadParams,original,posDiv));
	} else {
		bidix.upload.uploadMain(uploadParams,original,posDiv);
	}
};

bidix.upload.uploadMain = function(uploadParams,original,posDiv) 
{
	var callback = function(status,params,responseText,url,xhr) {
		var log = new bidix.UploadLog();
		if(status) {
			// if backupDir specified
			if ((params[3]) && (responseText.indexOf("backupfile:") > -1))  {
				var backupfile = responseText.substring(responseText.indexOf("backupfile:")+11,responseText.indexOf("\n", responseText.indexOf("backupfile:")));
				displayMessage(bidix.upload.messages.backupSaved,bidix.dirname(url)+'/'+backupfile);
			}
			var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
			displayMessage(bidix.upload.messages.mainSaved,bidix.dirname(url)+'/'+destfile);
			store.setDirty(false);
			log.endUpload("ok");
		} else {
			alert(bidix.upload.messages.mainFailed);
			displayMessage(bidix.upload.messages.mainFailed);
			log.endUpload("failed");			
		}
	};
	// do uploadMain
	var revised = bidix.upload.updateOriginal(original,posDiv);
	bidix.upload.httpUpload(uploadParams,revised,callback,uploadParams);
};

bidix.upload.httpUpload = function(uploadParams,data,callback,params)
{
	var localCallback = function(status,params,responseText,url,xhr) {
		url = (url.indexOf("nocache=") < 0 ? url : url.substring(0,url.indexOf("nocache=")-1));
		if (xhr.status == 404)
			alert(bidix.upload.messages.storePhpNotFound.format([url]));
		if ((bidix.debugMode) || (responseText.indexOf("Debug mode") >= 0 )) {
			alert(responseText);
			if (responseText.indexOf("Debug mode") >= 0 )
				responseText = responseText.substring(responseText.indexOf("\n\n")+2);
		} else if (responseText.charAt(0) != '0') 
			alert(responseText);
		if (responseText.charAt(0) != '0')
			status = null;
		callback(status,params,responseText,url,xhr);
	};
	// do httpUpload
	var boundary = "---------------------------"+"AaB03x";	
	var uploadFormName = "UploadPlugin";
	// compose headers data
	var sheader = "";
	sheader += "--" + boundary + "\r\nContent-disposition: form-data; name=\"";
	sheader += uploadFormName +"\"\r\n\r\n";
	sheader += "backupDir="+uploadParams[3] +
				";user=" + uploadParams[4] +
				";password=" + uploadParams[5] +
				";uploaddir=" + uploadParams[2];
	if (bidix.debugMode)
		sheader += ";debug=1";
	sheader += ";;\r\n"; 
	sheader += "\r\n" + "--" + boundary + "\r\n";
	sheader += "Content-disposition: form-data; name=\"userfile\"; filename=\""+uploadParams[1]+"\"\r\n";
	sheader += "Content-Type: text/html;charset=UTF-8" + "\r\n";
	sheader += "Content-Length: " + data.length + "\r\n\r\n";
	// compose trailer data
	var strailer = new String();
	strailer = "\r\n--" + boundary + "--\r\n";
	data = sheader + data + strailer;
	if (bidix.debugMode) alert("about to execute Http - POST on "+uploadParams[0]+"\n with \n"+data.substr(0,500)+ " ... ");
	var r = doHttp("POST",uploadParams[0],data,"multipart/form-data; ;charset=UTF-8; boundary="+boundary,uploadParams[4],uploadParams[5],localCallback,params,null);
	if (typeof r == "string")
		displayMessage(r);
	return r;
};

// same as Saving's updateOriginal but without convertUnicodeToUTF8 calls
bidix.upload.updateOriginal = function(original, posDiv)
{
	if (!posDiv)
		posDiv = locateStoreArea(original);
	if((posDiv[0] == -1) || (posDiv[1] == -1)) {
		alert(config.messages.invalidFileError.format([localPath]));
		return;
	}
	var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
				store.allTiddlersAsHtml() + "\n" +
				original.substr(posDiv[1]);
	var newSiteTitle = getPageTitle().htmlEncode();
	revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
	revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
	revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
	revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
	revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody");
	return revised;
};

//
// UploadLog
// 
// config.options.chkUploadLog :
//		false : no logging
//		true : logging
// config.options.txtUploadLogMaxLine :
//		-1 : no limit
//      0 :  no Log lines but UploadLog is still in place
//		n :  the last n lines are only kept
//		NaN : no limit (-1)

bidix.UploadLog = function() {
	if (!config.options.chkUploadLog) 
		return; // this.tiddler = null
	this.tiddler = store.getTiddler("UploadLog");
	if (!this.tiddler) {
		this.tiddler = new Tiddler();
		this.tiddler.title = "UploadLog";
		this.tiddler.text = "| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |";
		this.tiddler.created = new Date();
		this.tiddler.modifier = config.options.txtUserName;
		this.tiddler.modified = new Date();
		store.addTiddler(this.tiddler);
	}
	return this;
};

bidix.UploadLog.prototype.addText = function(text) {
	if (!this.tiddler)
		return;
	// retrieve maxLine when we need it
	var maxLine = parseInt(config.options.txtUploadLogMaxLine,10);
	if (isNaN(maxLine))
		maxLine = -1;
	// add text
	if (maxLine != 0) 
		this.tiddler.text = this.tiddler.text + text;
	// Trunck to maxLine
	if (maxLine >= 0) {
		var textArray = this.tiddler.text.split('\n');
		if (textArray.length > maxLine + 1)
			textArray.splice(1,textArray.length-1-maxLine);
			this.tiddler.text = textArray.join('\n');		
	}
	// update tiddler fields
	this.tiddler.modifier = config.options.txtUserName;
	this.tiddler.modified = new Date();
	store.addTiddler(this.tiddler);
	// refresh and notifiy for immediate update
	story.refreshTiddler(this.tiddler.title);
	store.notify(this.tiddler.title, true);
};

bidix.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir,  backupDir) {
	if (!this.tiddler)
		return;
	var now = new Date();
	var text = "\n| ";
	var filename = bidix.basename(document.location.toString());
	if (!filename) filename = '/';
	text += now.formatString("0DD/0MM/YYYY 0hh:0mm:0ss") +" | ";
	text += config.options.txtUserName + " | ";
	text += "[["+filename+"|"+location + "]] |";
	text += " [[" + bidix.basename(storeUrl) + "|" + storeUrl + "]] | ";
	text += uploadDir + " | ";
	text += "[[" + bidix.basename(toFilename) + " | " +toFilename + "]] | ";
	text += backupDir + " |";
	this.addText(text);
};

bidix.UploadLog.prototype.endUpload = function(status) {
	if (!this.tiddler)
		return;
	this.addText(" "+status+" |");
};

//
// Utilities
// 

bidix.checkPlugin = function(plugin, major, minor, revision) {
	var ext = version.extensions[plugin];
	if (!
		(ext  && 
			((ext.major > major) || 
			((ext.major == major) && (ext.minor > minor))  ||
			((ext.major == major) && (ext.minor == minor) && (ext.revision >= revision))))) {
			// write error in PluginManager
			if (pluginInfo)
				pluginInfo.log.push("Requires " + plugin + " " + major + "." + minor + "." + revision);
			eval(plugin); // generate an error : "Error: ReferenceError: xxxx is not defined"
	}
};

bidix.dirname = function(filePath) {
	if (!filePath) 
		return;
	var lastpos;
	if ((lastpos = filePath.lastIndexOf("/")) != -1) {
		return filePath.substring(0, lastpos);
	} else {
		return filePath.substring(0, filePath.lastIndexOf("\\"));
	}
};

bidix.basename = function(filePath) {
	if (!filePath) 
		return;
	var lastpos;
	if ((lastpos = filePath.lastIndexOf("#")) != -1) 
		filePath = filePath.substring(0, lastpos);
	if ((lastpos = filePath.lastIndexOf("/")) != -1) {
		return filePath.substring(lastpos + 1);
	} else
		return filePath.substring(filePath.lastIndexOf("\\")+1);
};

bidix.initOption = function(name,value) {
	if (!config.options[name])
		config.options[name] = value;
};

//
// Initializations
//

// require PasswordOptionPlugin 1.0.1 or better
bidix.checkPlugin("PasswordOptionPlugin", 1, 0, 1);

// styleSheet
setStylesheet('.txtUploadStoreUrl, .txtUploadBackupDir, .txtUploadDir {width: 22em;}',"uploadPluginStyles");

//optionsDesc
merge(config.optionsDesc,{
	txtUploadStoreUrl: "Url of the UploadService script (default: store.php)",
	txtUploadFilename: "Filename of the uploaded file (default: in index.html)",
	txtUploadDir: "Relative Directory where to store the file (default: . (downloadService directory))",
	txtUploadBackupDir: "Relative Directory where to backup the file. If empty no backup. (default: ''(empty))",
	txtUploadUserName: "Upload Username",
	pasUploadPassword: "Upload Password",
	chkUploadLog: "do Logging in UploadLog (default: true)",
	txtUploadLogMaxLine: "Maximum of lines in UploadLog (default: 10)"
});

// Options Initializations
bidix.initOption('txtUploadStoreUrl','');
bidix.initOption('txtUploadFilename','');
bidix.initOption('txtUploadDir','');
bidix.initOption('txtUploadBackupDir','');
bidix.initOption('txtUploadUserName','');
bidix.initOption('pasUploadPassword','');
bidix.initOption('chkUploadLog',true);
bidix.initOption('txtUploadLogMaxLine','10');


// Backstage
merge(config.tasks,{
	uploadOptions: {text: "upload", tooltip: "Change UploadOptions and Upload", content: '<<uploadOptions>>'}
});
config.backstageTasks.push("uploadOptions");


//}}}

Good reference manual: http://git-scm.com/book/en
Sourceforge help: https://sourceforge.net/p/forge/documentation/Git/

To set up a github repository:
#Log in to github
#Add new repository
#Set up .gitignore etc
#Click on clone to get url for the clone
#On PC {{{git clone <url>}}}
#Move/copy files into the repo
#{{{git add *; git status}}}
#{{{git commit -m 'initial upload'; git push origin master}}}

{{{git clone https://peterthevicar@github.com/peterthevicar/dementiadayclock.git dayclock}}} - clone a github repo into a subdirectory of current location

{{{git status}}} - tells what files are changed etc

{{{git log}}} - show log of commits
{{{git log -p -1}}} - with diffs (-p) only most recent (-1)

{{{git checkout HEAD^ -- <file.name>}}} - revert file to previous commit
{{{git checkout .; git pull}}} - revert everything to last commit
{{{git diff --cached}}} - difference between staged / reverted file and the current HEAD version

{{{git checkout -b <branchname>}}} - create and switch to a new branch
{{{git branch -v}}} - tells what branches there are and which one is active

Kernel build command:
{{{time make -j 4 deb-pkg LOCALVERSION=-pbcs}}}

Tagging versions
{{{git tag v3.1}}} - add tag to current state
{{{git tag -l}}} - list all tags

{{{git commit}}} - commit current staged changes
{{{git push}}} - send the committed changed up to the remote server (must have cloned using {{{ssh://}}} rather than {{{git://}}}, can change this in {{{.git/config}}} later)

Remove files from repository (leave local copy)
{{{
git rm -r --cached getridofthis
git commit -m 'Remove getridofthis from git'
git push origin master
}}}
 
Aim: Using scans of a series of double pages from a book, produce a printed booklet
Method:
#Save the scans with (e.g.) page numbers so that the scans sort in the correct order, let's say original scans are p01.jpg to p25.jpg
#Use convert with percentages to split the two pages in each scan: {{{convert -crop "50%x100%" p*.jpg q-%02d.jpg}}}
#This will produce 2 output files for each input file, the {{{%02d}}} is converted to a two digit number.
#The next step is to gather all those single pages into a pdf ready for printing. If you have pages you want as the front and back covers you will need to pad to a multiple of four pages with blanks ({{{xc:none}}}) like this:
#{{{convert -density 72 -page 'a5' front.jpg q*.jpg xc:none xc:none back.jpg A5.pdf}}}
#The density and page options are needed to scale the half pages to A5. "density" doesn't affect the resolution it just makes the "page" parameter work correctly, otherwise you may get very small pages.
#Finally open the pdf in Adobe Reader and print with page scaling set to booklet.
Problem: A4 pages are printed 1,2  3,4  5,6 etc and we want A5 1,2,3,4,5 to print as a booklet
[[PDF Booklet|http://pdfbooklet.sourceforge.net/]] can do this but it's not easy as you need to load each page twice. Doing that in the page ordering dialog doesn't work so you have to create a version of the input file with pages 1,1,2,2,3,4, etc using """PDFShuffler""". Not ideal.
Once you have the new input file:
#Open with """PDFBooklet"""
#Set the output page size manually to 297x210 (A4 landscape)
#In page transformation tab select "odd pages"
#Click on page 1 - should get a blue border
#Adjust rotation, zoom etc and shift the page to fill the page 1 slot (right hand side of A4 page)
#Select "even pages"
#Look through for an even page (not 0) and click on it
#Adjust to fill the left hand slot
#Check the document. If all is OK hit the Go button and save the booklet pdf file
#Install php
#Check by visiting localhost in firefox, should show apache2 default page
#Symlink local directory into /var/www/html
#Make sure www-data has execute access to every directory on the path to the local directory
#Access via localhost/subdir/test.php
#Install {{{project-x}}}
#Open the .rec file with ProjectX (in Multimedia menu)
#Use {{{File/Add...}}} to load the .rec file
#Set cut points
##Use the transport buttons ({{{I< <<< << <I I> >> >>> >I}}}) to find the positions for cut points
##Press the {{{+}}} button to set a cut point, {{{-}}} to remove. The green bars show what will be transferred, the red what won't. There is a drop-down list of cut points to the right of the {{{-}}} button, clicking on one of the numbers will take you to that point in the recording.
#Transcode
##Press the {{{prepare}}} button on the left (in the {{{Process}}} box)
##Choose {{{demux}}} or {{{to M2P}}} - {{{demux}}} gives the video and audio as separate files, {{{to M2P}}} gives a single file. Note the output file will overwrite any previously saved file so rename previous files if you are doing several cuts from the same file.

The output is fairly large, about 200MB for 5 minutes. To compress you can use avidemux. Choose:
*Video:
**"""MPEG-4 ASP (Xvid)"""
**Configure: use a higher number quantize for smaller, lower quality files (see below for table of results, generally better to compress more rather than trim frame size)
**Filters: Use [[Aspect Ratio calculations]] to set up the {{{MPlayer resize}}} filter (enter the Wdest as Width and Hdest as Height, leave both """ARs""" as 1:1; for 720x576 at 16:9 use 720x408 at 1:1 or 360x200 for smaller files and faster processing
*Audio:
**Audio / Build VBR Time Map
**"""MP2""" (Twolame)
**Time Shift -200 (check this)
*Format: """MP4"""

|!Quantize|!Frame size|!FPS processing|!MB/min|!Quality|
|4|720x408|25|16|full broadcast quality|
|6|720x408|17|10|fine|
|5|360x200|29|4|limits of usable|
|4|360x200|35|5|reasonable quality small|
|2|360x200|40|10|just OK for higher res screen, not as good as Q6 with full frame|
Use case: I want to use a simple web interface to get information to a computer / arduino controlling some automated device. This is an easy way to get the data into a text file which can then be polled by the controlling computer when it needs an update.
!!Cloud part
*Create a PHP form handler on your server to write the form contents to a text file. The file goes in the same directory as the PHP script.
{{{
cat form-process.php
<?php
if(isset($_POST)) {
    $ret = file_put_contents('form.json', json_encode($_POST, true));
    if($ret === false) {
        die('There was an error writing this file');
    }
    else {
        echo "$ret bytes written to file";
    }
}
else {
   die('no post data to process');
}
?>
}}}
*Create an html form on your server referencing the PHP handler above. Note this file must have a .php file name too.
{{{
$ cat form.php
<?php
$vars=json_decode(file_get_contents("form.json"), true);
?>
<html>
    <body>
        <h2>Form</h2>
        <form action="form-process.php" method="POST" autocomplete=off>
            <input name="var1" type="text" value="<?php echo $vars['var1']; ?>">
            <input name="var2" type="text" value="<?php echo $vars['var2']; ?>">
            <input type="submit" name="submit" value="Save Data">
        </form>
    </body>
</html>
}}}
!!Local part
*Use wget to get the file onto a PC/"""RPi""" connected to the Arduino. By default wget uses the same name for the downloaded file (in this example form.json)
{{{
$ wget salisburys.net/test/form.json -o /dev/null
$ cat form.json
{"var1":"e","var2":"d","submit":"Save Data"}
}}}
*Process the contents
*Communicate results to the Arduino (see [[Connect PC to raspberry pi to Arduino]])
Project-x sadly can't deal with HD recordings from """DVB-T2""" TV so you need to use avidemux.
#Load the ts file into avidemux
#Select copy for audio and video
#Select Mkv muxer for the container
#Mark unwanted sections such as adverts with start and end markers then delete
#Save cut file as an mkv video. This plays fine on a cheap TV with USB ports
Also see 
*[[Using ProjectX and avidemux to transcode Toppy .rec files]] which can cope with SD TS files recorded from """DVB-T"""
*[[DVB Subtitles]] 
*Install gtkmorph
*In terminal run gtkmorph
*In main window choose File/Add image
*In each image window choose an image (Image 1 is the 'from' image, Image 2 is the 'to' image)
*Put the image windows next to each other
*Click on features in image1 then move the highlighted dot in image2 to the corresponding place
*Once all the dots are done, in the main window choose Morph/Morph sequence
*Choose the number of frames for the morph and the output format
*(rm ~/frame*.png if you've done this before)
*Press play
*Sequence is stored in ~/frame.png etc
Now superseded by [[Forcing Megasync to do a backup instead of a sync]]

The cloud storage offered by MEGA is too good to be true, and the downside is that the megasync software is flaky when things get complex. I've ended up with files from one sync being put into the directories belonging to another, duplicated files, files turning into directories etc. On the plus side it's a lot of online storage for free so how to use it safely? In the end I've decided to use Linux permissions to make it a one-way sync, i.e. backup only, for most of my files. To do this run megasync as a user which doesn't have write access to your directories. Useful commands to check your files are at least readable by another user:
{{{find . -type d -not -perm /005 -ls}}} directories which can't be traversed or read by others
{{{find . -not -perm /004 -ls}}} files not readable by others
Fix up any you want to using chmod.
To run megasync as a different user, use this script as an autostart program:
{{{
#!/bin/bash
#Script to start megasync as a different user to avoid receiving any problems introduced at server
SYNCUSER=${1:-"x"}

xhost +si:localuser:$SYNCUSER
HOME=/home/$SYNCUSER sudo -u $SYNCUSER megasync # need this in the sudoers file: %sudo ALL=(x) NOPASSWD: /usr/bin/megasync
}}}
If you don't do the {{{xhost}}} thing it can't run on your screen. If you don't set the HOME variable you get a strange message from pango {{{Pango-WARNING **: error opening config file .config/pango/pangorc}}}
Call this script in the Print Settings / Output Options / Post-processing scripts. You will need to make it into an executable script and then put the script name in the dialog box in slic3r.
{{{
#!/bin/bash
# Process a gcode file to create a g3x file for the Replicator

if [ ! -f "$1" ]; then
  echo "usage: $0 filename"
fi

#Confirm gcode file
echo "G-code file"
ls -l $1

# Massage the file into a form suitable for gpx
BASF=${1%.*}
INTF="$BASF.gpx-gcode"
cat $1 |
  sed "s/;.*//" | # Strip the comments
  sed "s/^M108 T[01]$//" | # Take out the default tool change gcode which gpx doesn't like
  grep -v "^M70 P[0-9]*$" | # Remove M70 display commands which have no text specified
  grep -v "^$" >$INTF # remove blank lines
  
# Run gpx for Replicator 1 dual
gpx -g -m r1d $INTF $BASF.x3g
# Report
echo -en "G-code file:  $1\nModified:  $(date -r $1)\n\nOutput:  $(du -h $BASF.x3g)$(grep "filament used" $1)" |
zenity --text-info \
  --title "Summary" \
  --height=200 --width=600
}}}
Summary from https://www.domoticz.com/wiki/Setting_up_the_raspberry_pi_watchdog
{{{
sudo modprobe bcm2835_wdt
echo "bcm2835_wdt" | sudo tee -a /etc/modules
sudo nano /etc/watchdog.conf # enable watchdog service, set timeout (not 60s), set file checking if required (file updated every so many seconds or reboot)
sudo service watchdog start
}}}
Summary from http://iqjar.com/jar/sending-emails-from-the-raspberry-pi/
{{{
apt install ssmtp mail-utils
nano /etc/ssmtp/ssmtp.conf
}}}
nano /etc/ssmtp/revaliases # put in www-data:yourwebpagesname@your.domain:smtp.gmail.com:587
chmod 774 /etc/ssmtp/ssmtp.conf
}}}
Make sure www-data has read access to ssmtp.conf
{{{
root=postmaster
mailhub=smtp.gmail.com:587
hostname=raspberrypi
AuthUser=YourGMailUserName@gmail.com
AuthPass=YourGMailPassword
UseSTARTTLS=YES
}}}
Testing from command line:
{{{
echo “Test text” | mail -s “Test Mail” targetperson@example.com
}}}
Also has details of sending mail from php
See the source code for my dayclock program at https://github.com/peterthevicar/dementiadayclock/releases
http://www.thinkwiki.org/wiki/How_to_reduce_power_consumption
*In cpanel click on Web Disk button
*Create a new Web Disk user with files NOT in public_html
**New user, e.g. bill
**Strong password
**Directory not in public view, e.g. /webdisk/bill
**No need to enable digest security
*Gather login details
**Click on 'Client configuration'
**Click on any Linux file manager, e.g. nautilus
**Note the server address, port and username
*Put a test file in /webdisk/bill
*On Linux system
**Install davfs2 if not already done
**Use mount command and specify https (omitting it or using http gives {{{Could not read status line: Connection reset by peer}}})
**{{{sudo mount -t davfs https://<server address>:<port> mnt}}}
**Enter username (for me it's <name>@<server address> which is confusing!)
**Enter password
*Check it's worked: {{{ls mnt}}}

SADLY: this was for keepass2 and there's a problem with mono which won't allow https so had to use ftp
Install auditd and then set a watch on all the suspect directories on the drive. Use the same -k key to make searching easier, e.g.:
{{{
# auditctl -w /Disks/Data/ -k spinup -p rwxa
# auditctl -w /Disks/Scratch/ -k spinup -p rwxa
}}}

Then to search for events accessing those directories, just do {{{ausearch -k spinup | tail}}}
use {{{nethogs}}} or, less satisfactorily, netstat:
{{{
$ netstat -p --inet | grep ESTABLISHED
}}}
In xfce you can use a genmon panel applet to call this script:
{{{
#!/bin/bash
# A utility useable by the xfce genmon panel applet to give the top
# few processes using the network
NUM=${1:-2} # Number of lines to print, default 2
LEN=${2:-20} # Length of lines in characters, default 20
DEV=${3:-"eth0"} # Device to monitor, default eth0

TMPF="/tmp/nettop-out-$DEV"

# Step 1: collect the data from nethogs

# Sadly nethogs has no command line interface so we have to start it, wait a bit and send it a q.
# Need the TERM setting as it doesn't recognise 'dumb' from xfce generic monitor
(sleep 2; echo q) | TERM=xterm sudo nethogs -d 1 $DEV 2>/dev/null >$TMPF
#Put a dummy line as generic monitor get flustered if it gets no output
echo "SENT $DEV 3 4 5 6 7 8 -- 10 - -" >>$TMPF

# Step 2: Filter and format it

cat $TMPF |  
  grep "SENT" | grep $DEV |	head -n $NUM | # these are the lines we want, now strip out all the escape sequences
  sed "s/\x1b\(\[\(m\|H\|4l\|\?[0-9]*\(h\|l\)\|2J\|7h\|[0-9]\+;[0-9]\+.\|[0-9]\+G\)\|(B\|>\)/ /g; s/\xod/ /g" |
  # Now use awk to format the lines.
  awk -v linelen=$LEN '{
	  usr=$8; 
	  if (length(usr)>=9) # long usr names run into the program name and awk sees it as one field
	    {prog=substr(usr,10); i=10} 
	  else 
	    {prog=$9; i=11};
	  if (prog=="--") outp="--" # Nice simple output for dummy line
	  else {
		nf=split(prog, patha, "/"); prog=patha[nf]; # strip off the path
		outp = prog ":x" int($(i)) "r" int($(i+1)); # add input and output values
		}
	  if (length(outp) < linelen) outp = outp sprintf("%" linelen "s", " ");
	  printf ("%" linelen "." linelen "s\n", outp);
	  }'
}}}
Problem is to do with NM asking the dongle to switch MAC address for scanning and some don't like doing it. Workaround from https://bugs.launchpad.net/ubuntu/+source/network-manager/+bug/1681513

*Edit {{{/etc/NetworkManager/NetworkManager.conf}}}
*Add:
{{{
[device]
wifi.scan-rand-mac-address=no
}}}
*Restart the networkmanager service: {{{systemctl restart NetworkManager}}}
{{{
watch -n 1 cat /proc/net/wireless
}}}
run xev and have fun!

To see the logical keys (keysyms) understood by X, look in  {{{/usr/share/X11/XKeysymDB}}}

Use {{{getkeycodes}}} in a terminal to see non-recognised keycodes

From man xmodmap: The xmodmap program is used to edit and display the  keyboard  modifier map  and  keymap  table that are used by client applications to convert event keycodes into keysyms.  It is usually run from the user's session startup script to configure the keyboard according to personal tastes.

In Gnome: System / Preferences / Keyboard / Options has things like disable Caps Lock key
in xfce try in a terminal:
{{{xmodmap -d :0 -e "remove Lock = Caps_Lock"}}}
If that doesn't work then just assign the {{{NoSymbol}}} keysym to the appropriate keycode (you can find this by running xev and hitting the Caps Lock key in the little window it opens), for me it's 66, so:
{{{xmodmap -e "keycode 66 = NoSymbol"}}}
When you find which works for you, create an executable script with the line in and put it in Settings / Session and Startup / Application Autostart
In Debian based systems when you get the error:
{{{
Not Authorized. To start this action, an administrator password must be set and you must be logged in.
}}}
do this:
#{{{cd /etc/apt-cacher-ng}}}
#{{{nano security.conf}}}
#Set a username and password there (remove the # at the start of the line)
#{{{sudo /etc/init.d/apt-cacher-ng restart}}}
#Try the expiration again; you will be asked for the username/password you set in security.conf
Must use single quotes as the dollars have to get through to awk.
{{{
$ echo "a b c d e f g" | awk '{print $1 " " $3}'
a c
}}}
Or use escapes for the dollars and double quotes:
{{{
$ echo "first:53 second:25" | awk "{if (substr(\$1, 7, length(\$1)) > substr(\$2, 8, length(\$2))) {print \$1 \" bigger than \" \$2}}"
first:53 bigger than second:25
}}}
$0 refers to the whole input line
{{{
$ echo "a b c d" | awk "{print \$0}"
a b c d
}}}
For a bigger example of awk programming, see my program for extracting the readable contents of a TiddlyWiki
http://peterthevicar-archive.tiddlyspot.com/#%5B%5BExtracting%20the%20contents%20of%20a%20TiddlyWiki%20to%20a%20single%20file%20which%20is%20Google%20searchable%5D%5D
Very simple script; only works for sub one hour videos (assumes all timestamps are hh:mm). Change the {{{Style: Default}}} line for text size etc.
{{{
#!/usr/bin/awk -f
# Simple script to massage YouTube provided subtitles into an
# ass subtitle file which VLC can recognise
# In youtube copy the subtitles and paste into a text file, eg subs.txt
# Then use this script: ./subt.awk subs.txt >subs.ass

BEGIN {
    # Print a simple subtitles header
    print \
    "[Script Info]\n" \
    "ScriptType: v4.00+\n" \
    "WrapStyle: 0\n" \
    "ScaledBorderAndShadow: yes\n" \
    "YCbCr Matrix: TV.601\n" \
    "PlayResX: 1280\n" \
    "PlayResY: 720\n" \
    "[V4+ Styles]\n" \
    "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n" \
    "Style: Default,Arial,80,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,2,2,10,10,10,1\n" \
    "[Events]\n" \
    "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n";
    
    # Set up variables
    start_t=""; gob1="Dialogue: 0,0:"; gob2=".00,0:"; gob3=".00,Default,,0,0,0,,";
}
/^[0-9]/ {
    end_t=substr($0,1,5);
    if (start_t!="") {
        print gob1 start_t gob2 end_t gob3 subtext;
    }
    getline;
    subtext=$0;
    start_t=end_t;
}
}}}
Using awk split function to split a line using different field separators from the normal FS regexp (which can be set with -F on the command line)
{{{
wifi-signals | grep Quality | awk  '{split ($0, flds, "[/=]"); printf "%s,%i\%\n", $1, flds[2]/70*100}'
}}}
Program for reformatting an ics file from google calendar
{{{
#!/usr/bin/awk -f
BEGIN {split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec", months)}
/BEGIN:VCALENDAR|BEGIN:VEVENT|END:VEVENT|END:VCALENDAR/
/DTSTART;/ {sd=substr($0,index($0,":")+5,4); print}
/DTEND;/ {ed=substr($0,index($0,":")+5,4); print}
/SUMMARY:/ {
  sdm=substr(sd,1,2); sdd=gensub("^0","","",substr(sd,3,2))
  edm=substr(ed,1,2); edd=gensub("^0","","",substr(ed,3,2))-1
  if (edd<=0) {
    #TODO: mktime(ed)-8600 blah blah
    edm=edm-1
    }
  if (edm+0==sdm+0)
    if (edd+0==sdd+0)
      dates=sdd
    else
      dates=sdd "-" edd
  else
    dates=sdd "." months[sdm+0] "-" edd "." months[edm+0]
  printf("%s","SUMMARY:PS hol " dates "\n")
  }
}}}
A checksum file is formatted like this:
{{{
<File>::=<line>+
<line>::=<checksum><space>(<space>|<asterisk>)<filename>
}}}
asterisk means binary, space means don't care.
E.g.:
{{{
63787ffec0deb21ab587b5c66694aa0e *aosp-7.1-20170207-bokrom-hammerhead.zip
18896fb9f79410b82741dc25fd151eb0 *aosp-7.1-20170207-bokrom-gapps-hammerhead.zip
}}}
To use the file called file.md5: {{{md5sum -c file.md5}}}
{{{
Wireless      |--------|
Internet >>>> | Router |>>>>>> Wireless devices
Hotspot       |        |------ Wired devices
192.168.1.x   |--------|       192.168.2.x
}}}
This is for repeating wireless hotspots so that the hotspot only sees one connection, e.g. hotel networks. The instructions below work for a """D-Link DIR-615 rev d2""" with openwrt 15.05.1, """LuCI""" 15.05-142
#Install openwrt (see [[Install openwrt on D-Link DIR-615]])
#Connect to router via ethernet and go to 192.168.1.1 in browser - you get the """LuCI""" graphical interface
#set password
#Network/Interfaces, click edit for br-lan
##"""IPv4""" addr 192.168.@@2.99@@ (your choice, but must not be on the same network as the hotspot which is 192.168.@@1.x@@ in this case)
##"""IPv4""" gateway 192.168.1.1 (gateway for main network)
##Save and Apply
#Re-connect laptop to router to pick up an IP address in the new subnet (192.168.2.x in this example), sign in
#Network/"""WiFi"""/Scan
##{{{Join network}}} for the one you want to repeat
##Enter password if any
##({{{Replace wireless configuration}}} should not be checked, {{{Name of the new network}}} is {{{wwan}}} by default, should be in the {{{wan}}} firewall group)
##Apply - goes to Wireless client dialog
##Save and Apply - goes back to Wireless Overview dialog
#Click on the existing {{{OpenWRT}}} Master wireless entry - takes you to a setup dialog
##Check it's in AP mode and on same channel as Client (should be pre-filled correctly)
##Change """OpenWrt""" ESSID and/or encryption if desired
##Check {{{Network}}} is set to {{{lan}}} so the AP will broadcast the LAN side of the router
##Save and Apply
#To save mucking about, reboot the router

A sed script to read the whole file into the pattern buffer to do cross-line things (in this case replace line breaks with {{{<br>}}} tags)
{{{sed -n ':nl N; $ ! b nl; s/\n/<br>/g; p'}}}

Chunk up the input stream into three-line bits for processing (2 {{{N}}}s adds 2 more lines to the one already read). Note the final lines won't be processed if there are not 3.
{{{sed -n 'N; N; s/\n/<br>/g; p; d'}}}

A sed script for reversing the lines in a file:
{{{sed -n 'n; x; :nl n; x; H; $ ! b nl; x; p'}}}
In English: {{{n}}} read a line, {{{x}}} put in hold buffer. {{{:nl}}} label for processing each subsequent line. {{{n}}} read a line, {{{x}}} swap into hold buffer and {{{H}}} append the previous hold buffer contents. {{{$ ! b nl}}} if it's not {{{!}}} the last line {{{$}}} branch back {{{b nl}}} to read the next line, otherwise (it is the last line) {{{x}}} get the complete reversed file from the hold buffer and {{{p}}} print it out. As it's the last line, the script will terminate. You could add a {{{q}}} (quit) to make that clear.
Summary from https://www.smartmontools.org/wiki/BadBlockHowto
{{{sudo apt install smartmontools}}}
Dump the SMART data: {{{sudo smartctl -a /dev/sdb}}}
Look for {{{Current_Pending_Sector}}} if it's > 0 then there are bad sectors so:
#Use {{{sudo smartctl -t short /dev/sdb}}} to run a short test (takes about 2 minutes)
#Check result: {{{sudo smartctl --log=selftest /dev/sdb}}}
#Check with smartctl again and get the {{{LBA_of_first_error}}}
##Another way to check for faulty sectors (prints previous 20 errors) {{{sudo smartctl -l xerror,20 /dev/sdb | grep "LBA ="}}}
#Check this sector with hdparm: {{{sudo hdparm --read-sector <sector> /dev/sdb}}}
#It will usually fail, but even if not, write to it to force relocation:  {{{sudo hdparm --yes-i-know-what-i-am-doing --write-sector <sector> /dev/sdb}}}
#Check with smartctl again to see if the pending sector count has gone down.
#Repeat as necessary
#Long test: {{{sudo smartctl -t long /dev/sdb}}}. Takes ages (approx time given by {{{sudo smartctl --capabilities /dev/sdb | grep -A1 Extended}}}), check progress using:
{{{
$ sudo smartctl --capabilities /dev/sdb | grep -A 1 "Self-test execution"
Self-test execution status:      ( 247)	Self-test routine in progress...
					70% of test remaining.
}}}
http://rudd-o.com/linux-and-free-software/tales-from-responsivenessland-why-linux-feels-slow-and-how-to-fix-that says put this line into {{{/etc/sysctl.conf}}}
{{{
vm.swappiness=10
}}}
It also says to use 5 if you have less than 1GB memory
My cifs mounts were failing because they wait for the systemd target {{{network-online.target}}} but that was reached before """NetworkManager""" had configured the """WiFi""" connection so the mount failed. It's really a bug in the cifs mount code, especially its mad long timeout from a fail, but this delays the network-online target.
(Also see [[Insights into systemctl and systemd]])
Create a new systemd service as a dependency of the target and with a {{{Type=oneshot}}} command (this means systemd waits for it to complete before starting the next item. {{{RemainAfterExit=yes}}} is necessary for the {{{ExecStop}}} to be executed) that polls for ping access to the router. On shutdown it just sleeps 2 seconds to allow the shares to be umounted before the network is taken down. Again this is cifs fault for a silly long timeout on failure and/or not having systemd wait for it to umount.
{{{
$ cat /etc/systemd/system/network-online.target.wants/pbcs-wait-net.service 
[Unit]
Description=Wait for network to be up
Before=network-online.target
After=NetworkManager-wait-online.service

[Service]
Type=oneshot
RemainAfterExit=yes

ExecStart=/bin/bash -c "until ping -w1 -q -c1 192.168.1.1; do sleep 1; done"

ExecStop=/bin/sleep 2
}}}
Even with systemd you seem to need udev rules to do auto run of scripts on plugin of USB devices.
*Create the rule. Note that {{{/bin/echo}}} instead of {{{/bin/sh -c echo}}} didn't work in this example. Don't know why, could be the redirection.
{{{
$ cat /etc/udev/rules.d/90-pbcs-dell-7350-keyboard.rules
ACTION=="add", ATTRS{idVendor}=="045b", ATTRS{idProduct}=="0210", RUN+="/bin/sh -c 'echo test >/home/peter/test.txt'"
}}}
*Reload the udev rules
{{{
$ sudo udevadm control --reload
}}}
*Test the rule hits what you expect
{{{
$ udevadm trigger  -a idVendor="045b" -s usb -c add -v --dry-run
/sys/devices/pci0000:00/0000:00:14.0/usb1/1-1
/sys/devices/pci0000:00/0000:00:14.0/usb2/2-1
}}}
*Then if it's OK run without the dry-run option to test execution
You can put a launcher for this script onto an xfce panel:
{{{
~$ cat $(which brightness-gui)
#!/bin/bash
#GUI to set brightness for display in $1 using xrandr and redshift

DEV=${1:-"HDMI2"} # device to control - HDMI2 by default

#Find current brightness (xrandr gives 0 - 1, we need 0 - 100 for zenity)
CURB=$(xrandr --verbose | grep --max-count 1 -A5 $DEV | tail -n1 | FN=": " awk '{print $2;}')
CURBc=$(bc <<< "scale=0; $CURB * 100 / 1")

#Use zenity --scale to get a new brightness value
NEWBc=$(zenity --scale --title "Set $DEV brightness" --text "brightness (0-9 to reset)" --value=$CURBc)
if [[ "$NEWBc" < "10" ]]; then
    # Interpret 0-9 brightness as reset (redshift doesn't recognise anything below 0.1)
    killall redshift
    redshift -x
else
    NEWB=$(bc <<< "scale=2; $NEWBc / 100")
    
    #Use redshift to implement the new brightness
    killall redshift
    redshift -t 6500:3000 -b $NEWB -r
fi
}}}