Initial commit

This commit is contained in:
Phyks 2013-07-28 15:09:59 +02:00
commit c5c9f016fd
15 changed files with 804 additions and 0 deletions

38
LICENSE Normal file
View File

@ -0,0 +1,38 @@
This software (Remote) is licensed under the zlib/libpng License.
Copyright (c) 2013 Phyks and CCC
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
====================================================================================================================
The jquery library used is licensed under the MIT License (MIT)
Copyright 2013 jQuery Foundation and other contributors
http://jquery.com/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWAR

63
Launcher.sh Executable file
View File

@ -0,0 +1,63 @@
#!/bin/bash
# @nom : Launcher.sh
# @auteurs : Phyks (webmaster@phyks.me) and CCC (contact@cphyc.me)
# @description : Script to launch Remote Control
# See humans.txt file for more info
# Copyright (c) 2013 Phyks and CCC
# This software is licensed under the zlib/libpng License.
#Mute sound
echo "Mute sound"
amixer set Master mute > /dev/null
#Allow apache to access to display
echo "Allowing Apache to connect to the display..."
xhost +
#Launch apache
if ! systemctl status httpd > /dev/null; then
echo "Starting Apache... (may need your password)"
sudo systemctl start httpd
sleep 1
fi
echo "You can now connect to the following address using your browser : "
if [ -z "$1" ]
then
if ifconfig wlan0 | grep "inet " > /dev/null; then
echo http://$(ifconfig wlan0 | grep 'inet ' | cut -d: -f2 | awk '{print $2}')/Remote/
else
echo http://$(ifconfig eth0 | grep 'inet ' | cut -d: -f2 | awk '{print $2}')/Remote/
fi
else
echo "(Using interface $1)"
if ifconfig $1 | grep "inet " > /dev/null; then
echo http://$(ifconfig $1 | grep 'inet ' | cut -d: -f2 | awk '{print $2}')/Remote/
else
echo "The selected interface is not available. FATAL ERROR."
exit 1
fi
fi
#Pause
read -p "Press [Enter] key to quit..."
echo ""
echo "Now exiting..."
#Delete the tmp image
if test -s "tmp/tmp.png"; then
echo "Deleting temp files..."
rm -f tmp/tmp.png
fi
#Restore initial xhost configuration
echo "Restoring initial configuration"
xhost -
#Unmute sound
echo "Unmute sound"
amixer set Master unmute > /dev/null
exit 0

45
README Normal file
View File

@ -0,0 +1,45 @@
Remote
======
Remote allows you to remotely control any presentation (such as pdf files, diaporamas or impress.js presentations) with any device with a browser. It provides you an image of your screen, displayed on your (pocket) device and you can go through the slides.
- Application : Remote
- Version : 1.0
- Authors : Phyks (webmaster@phyks.me) and CCC (contact@cphyc.me)
- More info on http://phyks.me
- License : libpng/zlib (http://opensource.org/licenses/Zlib)
Pre-requisites
==============
- A webserver (Apache is used by default, but you can change it in Launcher.sh)
- PHP
- A Linux system because Windows makes easy things be very hard
Usage
=====
Just launch Launcher.sh and follow the guide :) It will configure everything properly and clean it after use. It will display the address to type using, by default wlan0 interface or eth0 if wlan0 is not available. You can force which interface you want to use by launching "Launcher.sh interface".
You can type a custom command in the specified field. For example, to simulate a specific key press, just type : "./remote.sh key $window" where remote.sh is the script that handle key press simulation (left or right for the buttons for example), key is the key you want to simulate and $window is a Remote-specific variable to act on the current window.
You can use some variables and parameters :
* $window will be replaced with the window currently selected
* --verbose will output the output of the command (always finish the command with --verbose)
Note : This version mutes sound when launched to avoid extra sounds played by Gnome when you use the arrows to navigate through your presentation. Just comment (#) the corresponding lines in Launcher.sh to avoid this behavior.
Troobleshooting :
=================
* You might see this error message :
"An error occured. Screenshot is not available.
Have you opened the presentation viewer ?"
If this happens and you shouldn't see it, then try to chmod the tmp folder within the Remote folder (Apache can't write inside by default). A "chmod 777 tmp" should work fine.
* If your browser can't find the address, maybe you renamed the "Remote" folder. Then, you can change manually the address opened in Launcher.sh to match your configuration.

2
TODO Executable file
View File

@ -0,0 +1,2 @@
Tester impress.js
Impress.js presentation directly in ?

104
base.css Executable file
View File

@ -0,0 +1,104 @@
/* --- Base Style for CSS --- */
/* Stylesheet to standardize the display of standard tags - Courtesy of Alsacreations ( http://www.alsacreations.com/astuce/lire/654-feuille-de-styles-de-base.html ) */
/* Feuille de style pour uniformiser l'affichage fournie par Alsacreations ( http://www.alsacreations.com/astuce/lire/654-feuille-de-styles-de-base.html ) */
/* Page */
html {
font-size: 100%; /* Évite un bug d'IE 6-7. (1) */
}
body {
margin: 0;
padding: 0; /* Remettre à zéro si nécessaire. */
font-family: Helvetica, sans-serif;
font-size: .8em;
line-height: 1.4; /* À adapter au design. (4) */
color: black;
background: white;
}
/* Titres */
h1, h2, h3, h4, h5, h6 {
margin: 1em 0 .5em 0; /* Rapproche le titre du texte.*/
line-height: 1.2;
font-weight: bold; /* Valeur par défaut.*/
font-style: normal;
}
h1 {
font-size: 1.75em;
}
h2 {
font-size: 1.5em;
}
h3 {
font-size: 1.25em;
}
h4 {
font-size: 1em;
}
hr {
height: 1px;
margin: 0;
margin-right: 1em;
padding: 0;
margin-bottom: 10px;
color: #000;
background-color: #000;
border: 0;
}
/* Listes */
ul, ol {
margin: .75em 0 .75em 0;
padding: 0;
}
/* Paragraphes */
p {
margin: .75em 0; /* Marges plus faibles que par défaut.*/
}
address {
margin: .75em 0;
font-style: normal;
}
/* Divers éléments de type en-ligne*/
em {
font-style: italic;
}
strong {
font-weight: bold;
}
/* Formulaires */
form, fieldset {
margin: 0;
padding: 0;
}
form
{
border: none;
}
input, button, select {
vertical-align: top; /* Solution pb. d'alignement. (9) */
font-size: 1em;
}
.center
{
text-align: center;
}
/* Mise en forme simple pour les tableaux */
table {
margin: 0;
border: 1px solid gray; /* Pas de bordure = "none". */
border-collapse: collapse; /* Valeur par défaut: "separate". */
border-spacing: 0;
margin: auto;
}
table td, table th {
padding: 4px; /* Pas de retrait autour du texte = "0". */
border: 1px solid #ccc; /* Pas de bordure = "none". */
vertical-align: top; /* Valeur par défaut: "middle" */
text-align: left;
}

140
design.css Executable file
View File

@ -0,0 +1,140 @@
/*
@nom: Design
@auteurs: Phyks (webmaster@phyks.me) and CCC (contact@cphyc.me)
@description: Main stylesheet
See humans.txt file for more info
Copyright (c) 2013 Phyks and CCC
This software is licensed under the zlib/libpng License.
*/
.underline
{
text-decoration: underline;
}
form
{
font-size: 2.5em;
}
input
{
vertical-align: middle;
}
#left, #right
{
width:50px;
position: absolute;
top: 35%;
margin: 0;
}
body
{
overflow-x: hidden;
width: 90%;
}
#left
{
left: 0;
}
#right
{
right: 0;
}
#background
{
position: absolute;
top: 0;
text-align: center;
height: 100%;
width: 100%;
margin:0;
}
#background img
{
max-width: 100%;
max-height: 100%;
}
#output
{
margin: auto;
width: 100%;
text-align: center;
font-size: 2.5em;
}
#output p
{
margin-top: 1em;
}
#output a
{
border: 2px solid black;
padding: 0.5em;
}
#error
{
text-align: center;
font-size: 2.5em;
font-weight: bold;
background-color: red;
color: white;
}
#commandline
{
position: absolute;
bottom: 5px;
left: 0;
margin: 0;
font-size: 3em;
}
#commandline input
{
text-align: center;
}
#commandline p
{
display: inline;
}
#submit
{
font-weight: bold;
}
#quit
{
position: absolute;
bottom: 0;
right: 0;
margin: 0;
font-size: 3em;
}
#refresh
{
position: absolute;
top: 0;
right: 0;
margin: 0;
font-size: 4em;
}
#refresh a
{
text-decoration: none;
}

29
humans.txt Normal file
View File

@ -0,0 +1,29 @@
/* TEAM */
Name : Phyks
Site: http://www.phyks.me
E-mail : webmaster@phyks.me
Jabber :
Location: France.
Name : CCC
Location : France
Site : http://www.cphyc.me
E-mail : contact@cphyc.me
/* THANKS */
Jquery.org for the jquery.js script file (under MIT License)
Alsacreations for the base stylesheet
PADILICIOUS.COM for the swipe.js script to handle touch events
Wikimedia Foundations on which website we found the arrows icons (modified) under GNU Lesser General Public License
/* LICENSE */
Copyright (c) 2013 Phyks and CCC
This software is licensed under the zlib/libpng License.
/* SITE */
Last update: 02/11/2012
Standards: HTML5, CSS3
Components: jQuery
Software: Arch, Fedora, Gnome Shell, gedit, php, apache and xdotool !

252
index.php Executable file
View File

@ -0,0 +1,252 @@
<?php
// @nom: Index
// @auteurs: Phyks (webmaster@phyks.me) and CCC (contact@cphyc.me)
// @description: Main page
// See humans.txt file for more info
// Copyright (c) 2013 Phyks and CCC
// This software is licensed under the zlib/libpng License.
session_start();
if(!empty($_GET['quit'])) //If we want to go back to the presentation choice form
{
session_destroy();// Destroy the session
header('location: index.php'); //Reload the page
}
if(!empty($_POST['window'])) //If we chose a specific window, let's save it !
{
$_SESSION['window'] = (int) $_POST['window'];
header('location: index.php');
}
//------ Browser specific code ---------
//Some code to detec iPhone and put adequate tags
$browser = '';
if (strpos($_SERVER['HTTP_USER_AGENT'],"iPhone")) //Detect whether browser is Safari Mobile or not (to add correct tags)
$browser = 'iphone';
//--------------------------------------
//------ Orders ---------
//If an order is given (left or right), we execute it and then redirect to this page without arguments in $_GET
if(!empty($_GET['left']))
{
shell_exec('./remote.sh Left '.$_SESSION['window']);
usleep(2000000);
header('location:index.php');
exit;
}
elseif(!empty($_GET['right']))
{
shell_exec('./remote.sh Right '.$_SESSION['window']);
usleep(2000000);
header('location:index.php');
exit;
}
//--------------------------------------
////----------- Get the background image ----------------
if(is_file("tmp/tmp.png")) //Delete the old background file if needed
{
unlink("tmp/tmp.png");
}
//First, we get the ids of all the windows with a title containing ".pdf" (e.g. Evince used to read a pdf document) OR of the window we specified by the $_SESSION var
if(!empty($_SESSION['window']))
{
$ids = (int) $_SESSION['window'];
if(!empty($_POST['command'])) //If custom command passed
{
$command = str_replace('$window', $ids, $_POST['command']);
if(strpos($command, '--verbose') !== FALSE)
{
$command = substr($command, 0, strpos($command, '--verbose'));
$output = shell_exec($command);
}
else
{
shell_exec($command);
header('location: index.php');
exit;
}
}
}
else
{
$ids = shell_exec("export DISPLAY=:0 && xdotool search --name --desktop 0 \"\"");
}
$ids = array_filter(explode("\n", $ids)); //Get the ids in array form and delete empty entries
if(count($ids) == 1) //If there's only one window which is ok -> easy :)
{
$ids = (int) $ids[0]; //We force ids to be int
shell_exec("export DISPLAY=:0 && import -window ".$ids." tmp/tmp.png"); //We take a screenshot for the background
$titre = shell_exec("export DISPLAY=:0 && xdotool getwindowname ".$ids); //Get the whole name to forge the title of this page
}
elseif(count($ids) > 1) //If there are more than one window with "pdf" in the title, display a form to choose which one you want
{
$form_content = '';
$i = 1;
$checked = '';
foreach($ids as $id)
{
$name = shell_exec("export DISPLAY=:0 && xdotool getwindowname ".$id);
if($i == 1)
{
$checked = 'checked';
$i = 2;
}
$form_content .= "<input type='radio' name='window' value='".$id."' id='".$id."' ".$checked."/><label for='".$id."'>".$name."</label><br/>"; //Fill a variable containing the radio fields
$checked = '';
}
}
//If $ids == NULL -> no window ok -> Just display an error and put a correct title
?>
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title><?php
if(!empty($titre)) echo $titre; elseif(!empty($form_content)) echo "Remote Presentation"; else echo "Error - Remote Presentation"; ?></title>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="swipe.js"></script>
<script type="text/javascript">
function reset_commandline()
{
var commandline = document.getElementById('commandline').elements['command'];
commandline.onclick = function ()
{
if(commandline.value == commandline.defaultValue)
{
commandline.value = '';
}
};
commandline.onblur = function()
{
if(commandline.value == '')
{
commandline.value = commandline.defaultValue;
}
};
}
if(document.getElementById && document.createTextNode)
{
window.onload = function()
{
reset_commandline();
};
}
</script>
<link rel="stylesheet" type="text/css" href="base.css" />
<link rel="stylesheet" type="text/css" href="design.css" />
<link rel="author" href="humans.txt" />
<?php if(!empty($_SESSION['window'])) { ?>
<meta http-equiv="Refresh" content="10; url=index.php" /> <!-- Redirection toutes les 30 secondes pour actualiser l'affichage -->
<?php }?>
<?php if($browser == 'iphone'){ ?>
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="viewport" content="width=device-width, minimum-scale=1.0, user-scalable=no" />
<link rel="apple-touch-icon" href="iphone.png" />
<script type="text/javascript">
$(document).ready(function() {
window.scrollTo(0, 1);
$("a").click(function (event) {
if ( (navigator.standalone)
&&
((navigator.userAgent.indexOf("iPhone") > -1) || (navigator.userAgent.indexOf("iPad") > -1))
) {
//Prevent iOs from quitting the fullscreen mode when opening a link
event.preventDefault();
window.location = $(this).attr("href");
}
});
});
</script>
<?php } ?>
</head>
<body>
<?php
if(!empty($output))
{
echo '<div id="output"><pre>'.$output.'</pre><p><a href="index.php">Go back</a></p></div></body></html>';
exit;
}
if(is_file('tmp/tmp.png')) //Protection against the "img not found" little frame
{
?>
<p id="background"" ontouchstart="touchStart(event,'background');" ontouchend="touchEnd(event); if(swipeDirection == 'left') { window.location='?right=1'; } if(swipeDirection == 'right') { window.location='?left=1'; }" ontouchmove="touchMove(event);" ontouchcancel="touchCancel(event);"><img src="tmp/tmp.png"/></p>
<?php
}
else //Display an error if we don't display the form
{
if(empty($form_content))
{
?>
<div id='error'>
<p>An error occured. Screenshot is not available.</p>
<p>Have you opened the selected presentation viewer ?</p>
</div>
<?php
}
}
if(!empty($titre)) //Else, display the commands
{
?>
<div id="left">
<p>
<a href='?left=1'><img src='left.png' alt='Gauche' style='width: 50px;'/></a>
</p>
</div>
<div id="right">
<p>
<a href='?right=1'><img src='right.png' alt='Droite' style='width: 50px;'/></a>
</p>
</div>
<?php
}
if(!empty($form_content)) { //If needed, display the form ?>
<form method="post" action="index.php" id="background">
<p>
<span class="underline">Window to work with ?</span>
</p>
<p>
<?php echo $form_content; ?>
</p>
<p>
<input type='submit' value='↳'/>
</p>
</form>
<?php }
if(!empty($_SESSION['window'])) //And if $_SESSION is set, display a link to go back to the window choice
{
?>
<form method="post" action="index.php" id="commandline">
<p>
<input type="text" name="command" value="Custom command" size="12"/>
<input type="submit" id="submit" value=""/>
</p>
</form>
<p id="quit"><a href="?quit=1">Go back to form</a></p>
<p id="refresh"><a href="index.php"></a></p>
<?php
}
?>
</body>
</html>

BIN
iphone.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

2
jquery.js vendored Executable file

File diff suppressed because one or more lines are too long

BIN
left.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

14
remote.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
# @nom : Remote.sh
# @auteurs : Phyks (webmaster@phyks.me) and CCC (contact@cphyc.me)
# @description : Script to handle left and right move
# See humans.txt file for more info
# Copyright (c) 2013 Phyks and CCC
# This software is licensed under the zlib/libpng License.
export DISPLAY=:0
WID=$2
/usr/bin/xdotool windowfocus $WID
/usr/bin/xdotool key $1

BIN
right.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

2
robots.txt Executable file
View File

@ -0,0 +1,2 @@
User-Agent: *
Disallow: /

113
swipe.js Executable file
View File

@ -0,0 +1,113 @@
// TOUCH-EVENTS SINGLE-FINGER SWIPE-SENSING JAVASCRIPT
// Courtesy of PADILICIOUS.COM and MACOSXAUTOMATION.COM
var fingerCount = 0;
var startX = 0;
var startY = 0;
var curX = 0;
var curY = 0;
var deltaX = 0;
var deltaY = 0;
var horzDiff = 0;
var vertDiff = 0;
var minLength = 100;//72 = default
var swipeLength = 0;
var swipeAngle = null;
var swipeDirection = null;
var triggerElementID = null;
// The 4 Touch Event Handlers
// the touchStart handler should also receive the ID of the triggering element
// make sure it's ID is passed in the event call placed in the element declaration, like:
// <div id="picture-frame" ontouchstart="touchStart(event,'picture-frame');" ontouchend="touchEnd(event);" ontouchmove="touchMove(event);" ontouchcancel="touchCancel(event);">
function touchStart(event,passedID) {
// disable the standard ability to select the touched object
//event.preventDefault();
// get the total number of fingers touching the screen
fingerCount = event.touches.length;
// since we're looking for a swipe (single finger) and not a gesture (multiple fingers),
// check that only one finger was used
if ( fingerCount == 1 ) {
// store the triggering element ID
triggerElementID = passedID;
// get the coordinates of the touch
startX = event.touches[0].pageX;
startY = event.touches[0].pageY;
} else {
// more than one finger touched so cancel
touchCancel(event);
}
}
function touchMove(event) {
//event.preventDefault();
if ( event.touches.length == 1 ) {
curX = event.touches[0].pageX;
curY = event.touches[0].pageY;
} else {
touchCancel(event);
}
}
function touchEnd(event) {
//event.preventDefault();
// check to see if more than one finger was used and that there is a and ending coordinate
if ( fingerCount == 1 && curX != 0 ) {
// use the Distance Formula to determine the length of the swipe
swipeLength = Math.round(Math.sqrt(Math.pow(curX - startX,2) + Math.pow(curY - startY,2)));
horzDiff = Math.abs(startX - curX);
vertDiff = Math.abs(startY - curY);
if ( swipeLength >= minLength ) {
deltaX = startX - curX;
deltaY = startY - curY;
caluculateAngle();
determineSwipeDirection();
} else {
touchCancel(event);
}
} else {
touchCancel(event);
}
}
function touchCancel(event) {
// reset the variables back to default values
fingerCount = 0;
startX = 0;
startY = 0;
curX = 0;
curY = 0;
deltaX = 0;
deltaY = 0;
horzDiff = 0;
vertDiff = 0;
swipeLength = 0;
swipeAngle = null;
swipeDirection = null;
triggerElementID = null;
}
function caluculateAngle() {
var X = startX-curX;
var Y = curY-startY;
var Z = Math.round(Math.sqrt(Math.pow(X,2)+Math.pow(Y,2))); //the distance - rounded - in pixels
var r = Math.atan2(Y,X); //angle in radians (Cartesian system)
swipeAngle = Math.round(r*180/Math.PI); //angle in degrees
if ( swipeAngle < 0 ) { swipeAngle = 360 - Math.abs(swipeAngle); }
}
function determineSwipeDirection() {
if ( (swipeAngle <= 45) && (swipeAngle >= 0) ) {
swipeDirection = 'left';
} else if ( (swipeAngle <= 360) && (swipeAngle >= 315) ) {
swipeDirection = 'left';
} else if ( (swipeAngle >= 135) && (swipeAngle <= 225) ) {
swipeDirection = 'right';
} else if ( (swipeAngle > 45) && (swipeAngle < 135) ) {
swipeDirection = 'down';
} else {
swipeDirection = 'up';
}
}