├── README.md ├── bg.jpg ├── font ├── LucidaGrande.ttf └── LucidaGrandeBold.ttf ├── img ├── avatar.svg ├── restart.svg ├── shutdown.svg └── sleep.svg ├── index.html ├── index.theme ├── mock.js ├── script.js └── style.css /README.md: -------------------------------------------------------------------------------- 1 | LightDM Webkit MacOSX Theme 2 | =========================== 3 | 4 | This is a LightDM theme for the Webkit greeter which tries to imitate the look and feel of the Mac OSX lion login screen 5 | 6 | Demo: 7 | ------------------------- 8 | 9 | http://www.youtube.com/watch?v=1Frf3QlZ_gw 10 | 11 | 12 | Installation Instructions 13 | ------------------------- 14 | You will need LightDM as your login manager. On newer versions of Ubuntu this is already the default. Additionally you will require the webkit greeter. This is done by executing the following command in the command line: 15 | 16 |
17 | sudo apt-get install lightdm-webkit-greeter
18 | 
19 | 20 | Once the installation finishes, you need to make the webkit greeter the default greeter. This is done by editing the lightdm configuration under: 21 | 22 |
23 | /etc/lightdm/lightdm.conf
24 | 
25 | 26 | and changing the greeter-session value to lightdm-webkit-greeter. My lightdm.conf looks like: 27 | 28 | [SeatDefaults] 29 | greeter-session=lightdm-webkit-greeter 30 | allow-guest=false 31 | . 32 | 33 | The second step is to install the actual theme. This is done by copying the files of this repository into the following location: 34 | 35 |
36 | /usr/share/lightdm-webkit/themes/mac
37 | 
38 | 39 | Finally, change the /etc/lightdm/lightdm-webkit-greeter.conf file to contain the following line: 40 | 41 |
42 | webkit-theme=mac 
43 | 
44 | 45 | Now you can reboot and enjoy the new theme. 46 | -------------------------------------------------------------------------------- /bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wattos/LightDM-Webkit-MacOSX-Theme/8a886f1a9452a4c5bd777abd93ee84839c3d9dee/bg.jpg -------------------------------------------------------------------------------- /font/LucidaGrande.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wattos/LightDM-Webkit-MacOSX-Theme/8a886f1a9452a4c5bd777abd93ee84839c3d9dee/font/LucidaGrande.ttf -------------------------------------------------------------------------------- /font/LucidaGrandeBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wattos/LightDM-Webkit-MacOSX-Theme/8a886f1a9452a4c5bd777abd93ee84839c3d9dee/font/LucidaGrandeBold.ttf -------------------------------------------------------------------------------- /img/avatar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | image/svg+xml 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /img/restart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 28 | 32 | 33 | 40 | 44 | 45 | 46 | 64 | 66 | 67 | 69 | image/svg+xml 70 | 72 | 73 | 74 | 75 | 76 | 81 | 98 | 111 | 128 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /img/shutdown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 24 | 28 | 29 | 36 | 40 | 41 | 44 | 48 | 49 | 56 | 60 | 61 | 64 | 68 | 69 | 72 | 76 | 77 | 80 | 84 | 85 | 92 | 96 | 97 | 104 | 108 | 109 | 116 | 120 | 121 | 122 | 140 | 142 | 143 | 145 | image/svg+xml 146 | 148 | 149 | 150 | 151 | 152 | 157 | 170 | 183 | 190 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /img/sleep.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 28 | 32 | 33 | 40 | 44 | 45 | 52 | 56 | 57 | 58 | 76 | 78 | 79 | 81 | image/svg+xml 82 | 84 | 85 | 86 | 87 | 88 | 93 | 106 | 113 | 126 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 |
14 | 18 |
19 | 24 |
25 |
26 |
27 |
28 |
29 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /index.theme: -------------------------------------------------------------------------------- 1 | [theme] 2 | name=MacOsX 3 | description=Mac OSX like logon 4 | engine=lightdm-webkit-greeter 5 | url=index.html 6 | session=gnome 7 | -------------------------------------------------------------------------------- /mock.js: -------------------------------------------------------------------------------- 1 | // mock lighdm for testing 2 | if (typeof lightdm == 'undefined') { 3 | lightdm= {}; 4 | lightdm.hostname="test-host"; 5 | lightdm.languages= [{code: "en_US", name: "English(US)", territory: "USA"}, {code: "en_UK", name: "English(UK)", territory: "UK"}]; 6 | lightdm.default_language= lightdm.languages[0]; 7 | lightdm.layouts= [{name: "test", short_description: "test description", short_description:"really long epic description"}]; 8 | lightdm.default_layout= lightdm.layouts[0]; 9 | lightdm.layout= lightdm.layouts[0]; 10 | lightdm.sessions=[{key: "key1", name: "session 1", comment: "no comment"}, {key: "key2", name: "session 2", comment: "no comment"}]; 11 | 12 | lightdm.default_session=lightdm.sessions[0]; 13 | lightdm.authentication_user= null; 14 | lightdm.is_authenticated= false; 15 | lightdm.can_suspend= true; 16 | lightdm.can_hibernate= true; 17 | lightdm.can_restart= true; 18 | lightdm.can_shutdown= true; 19 | 20 | lightdm.users= [ 21 | { name: "clarkk", real_name:"Superman", display_name: "Clark Kent", image :"", language: "en_US", layout: null, session: null, logged_in: false }, 22 | { name: "brucew", real_name:"Batman", display_name: "Bruce Wayne", image :"/home/brokenImage.gif", language: "en_US", layout: null, session: null, logged_in: false}, 23 | { name: "peterp", real_name:"Spiderman", display_name: "Peter Parker", image :"", language: "en_US", layout: null, session: null, logged_in: true}, 24 | ] 25 | 26 | lightdm.num_users= lightdm.users.length; 27 | lightdm.timed_login_delay= 0; //set to a number higher than 0 for timed login simulation 28 | lightdm.timed_login_user= lightdm.timed_login_delay > 0 ? lightdm.users[0] : null; 29 | 30 | lightdm.get_string_property= function() {}; 31 | lightdm.get_integer_property= function() {}; 32 | lightdm.get_boolean_property= function() {}; 33 | lightdm.cancel_timed_login= function() { 34 | _lightdm_mock_check_argument_length(arguments, 0); 35 | lightdm._timed_login_cancelled= true; 36 | }; 37 | 38 | lightdm.provide_secret= function(secret) { 39 | if (typeof lightdm._username == 'undefined' || !lightdm._username) { 40 | throw "must call start_authentication first" 41 | } 42 | _lightdm_mock_check_argument_length(arguments, 1); 43 | var user= _lightdm_mock_get_user(lightdm.username); 44 | 45 | if (!user && secret == lightdm._username) { 46 | lightdm.is_authenticated= true; 47 | lightdm.authentication_user= user; 48 | } else { 49 | lightdm.is_authenticated= false; 50 | lightdm.authentication_user= null; 51 | lightdm._username= null; 52 | } 53 | authentication_complete(); 54 | }; 55 | 56 | lightdm.start_authentication= function(username) { 57 | _lightdm_mock_check_argument_length(arguments, 1); 58 | if (lightdm._username) { 59 | throw "Already authenticating!"; 60 | } 61 | var user= _lightdm_mock_get_user(username); 62 | if (!user) { 63 | show_error(username + " is an invalid user"); 64 | } 65 | show_prompt("Password: "); 66 | lightdm._username= username; 67 | }; 68 | 69 | lightdm.cancel_authentication= function() { 70 | _lightdm_mock_check_argument_length(arguments, 0); 71 | if (!lightdm._username) { 72 | throw "we are not authenticating"; 73 | } 74 | lightdm._username= null; 75 | }; 76 | 77 | lightdm.suspend= function() { 78 | alert("System Suspended. Bye Bye"); 79 | document.location.reload(true); 80 | }; 81 | 82 | lightdm.hibernate= function() { 83 | alert("System Hibernated. Bye Bye"); 84 | document.location.reload(true); 85 | }; 86 | 87 | lightdm.restart= function() { 88 | alert("System restart. Bye Bye"); 89 | document.location.reload(true); 90 | }; 91 | 92 | lightdm.shutdown= function() { 93 | alert("System Shutdown. Bye Bye"); 94 | document.location.reload(true); 95 | }; 96 | 97 | lightdm.login= function(user, session) { 98 | _lightdm_mock_check_argument_length(arguments, 2); 99 | if (!lightdm.is_authenticated) { 100 | throw "The system is not authenticated"; 101 | } 102 | if (user !== lightdm.authentication_user) { 103 | throw "this user is not authenticated"; 104 | } 105 | alert("logged in successfully!!"); 106 | document.location.reload(true); 107 | }; 108 | 109 | if (lightdm.timed_login_delay > 0) { 110 | setTimeout(function() { if (!lightdm._timed_login_cancelled()) timed_login();}, lightdm.timed_login_delay); 111 | } 112 | } 113 | 114 | function _lightdm_mock_check_argument_length(args, length) { 115 | if (args.length != length) { 116 | throw "incorrect number of arguments in function call"; 117 | } 118 | } 119 | 120 | function _lightdm_mock_get_user(username) { 121 | var user= null; 122 | for (var i= 0; i < lightdm.users.length; ++i) { 123 | if (lightdm.users[i].name == username) { 124 | user= lightdm.users[i]; 125 | break; 126 | } 127 | } 128 | return user; 129 | } -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | time_remaining = 0; 2 | selected_user= null; 3 | valid_image=/.*\.(png|svg|jpg|jpeg|bmp)$/i; 4 | 5 | /////////////////////////////////////////////// 6 | // CALLBACK API. Called by the webkit greeeter 7 | /////////////////////////////////////////////// 8 | 9 | // called when the greeter asks to show a login prompt for a user 10 | function show_prompt(text) { 11 | var password_container= document.getElementById("password_container"); 12 | var password_entry= document.getElementById("password_entry"); 13 | if (!isVisible(password_container)) { 14 | var users= document.getElementsByClassName("user"); 15 | var user_node= document.getElementById(selected_user); 16 | var rect= user_node.getClientRects()[0]; 17 | var parentRect= user_node.parentElement.getClientRects()[0]; 18 | var center= parentRect.width/2; 19 | var left= center - rect.width/2 - rect.left; 20 | if (left < 5 && left > -5) { 21 | left= 0; 22 | } 23 | for (var i= 0; i < users.length; i++) { 24 | var node= users[i]; 25 | setVisible(node, node.id == selected_user); 26 | node.style.left= left; 27 | } 28 | 29 | setVisible(password_container, true); 30 | password_entry.placeholder= text.replace(":", ""); 31 | } 32 | password_entry.value= ""; 33 | password_entry.focus(); 34 | } 35 | 36 | // called when the greeter asks to show a message 37 | function show_message(text) { 38 | var message= document.getElementById("message_content"); 39 | message.innerHTML= text; 40 | if (text) { 41 | document.getElementById("message").classList.remove("hidden"); 42 | } else { 43 | document.getElementById("message").classList.add("hidden"); 44 | } 45 | message.classList.remove("error"); 46 | } 47 | 48 | // called when the greeter asks to show an error 49 | function show_error(text) { 50 | show_message(text); 51 | var message= document.getElementById("message_content"); 52 | message.classList.add("error"); 53 | } 54 | 55 | // called when the greeter is finished the authentication request 56 | function authentication_complete() { 57 | if (lightdm.is_authenticated) 58 | lightdm.login (lightdm.authentication_user, lightdm.default_session); 59 | else { 60 | show_error("Authentication Failed"); 61 | start_authentication(selected_user); 62 | } 63 | } 64 | 65 | // called when the greeter wants us to perform a timed login 66 | function timed_login(user) { 67 | lightdm.login (lightdm.timed_login_user); 68 | setTimeout('throbber()', 1000); 69 | } 70 | 71 | ////////////////////////////// 72 | // Implementation 73 | ////////////////////////////// 74 | function start_authentication(username) { 75 | lightdm.cancel_timed_login(); 76 | selected_user= username; 77 | lightdm.start_authentication(username); 78 | } 79 | 80 | function provide_secret() { 81 | show_message("Logging in..."); 82 | entry = document.getElementById('password_entry'); 83 | lightdm.provide_secret(entry.value); 84 | } 85 | 86 | function show_users() { 87 | var users= document.getElementsByClassName("user"); 88 | for (var i= 0; i < users.length; i++) { 89 | setVisible(users[i], true); 90 | users[i].style.left= 0; 91 | } 92 | setVisible(document.getElementById("password_container"), false); 93 | selected_user= null; 94 | } 95 | 96 | function user_clicked(event) { 97 | if (selected_user != null) { 98 | selected_user= null; 99 | lightdm.cancel_authentication(); 100 | show_users(); 101 | } else { 102 | selected_user= event.currentTarget.id; 103 | start_authentication(event.currentTarget.id); 104 | } 105 | show_message(""); 106 | event.stopPropagation(); 107 | return false; 108 | } 109 | 110 | function setVisible(element, visible) { 111 | if (visible) { 112 | element.classList.remove("hidden"); 113 | } else { 114 | element.classList.add("hidden"); 115 | } 116 | } 117 | 118 | function isVisible(element) { 119 | return !element.classList.contains("hidden"); 120 | } 121 | 122 | function update_time() { 123 | var time= document.getElementById("current_time"); 124 | var date= new Date(); 125 | 126 | var hh = date.getHours(); 127 | var mm = date.getMinutes(); 128 | var ss = date.getSeconds(); 129 | var suffix= "AM"; 130 | if (hh > 12) { 131 | hh= hh - 12; 132 | suffix= "PM"; 133 | } 134 | if (hh < 10) {hh = "0"+hh;} 135 | if (mm < 10) {mm = "0"+mm;} 136 | if (ss < 10) {ss = "0"+ss;} 137 | time.innerHTML= hh+":"+mm + " " + suffix; 138 | } 139 | 140 | ////////////////////////////////// 141 | // Initialization 142 | ////////////////////////////////// 143 | 144 | function initialize() { 145 | show_message(""); 146 | initialize_users(); 147 | initialize_actions(); 148 | initialize_timer(); 149 | } 150 | 151 | function initialize_users() { 152 | var template= document.getElementById("user_template"); 153 | var parent= template.parentElement; 154 | parent.removeChild(template); 155 | 156 | for (i in lightdm.users) { 157 | user= lightdm.users[i]; 158 | userNode= template.cloneNode(true); 159 | 160 | var image= userNode.getElementsByClassName("user_image")[0]; 161 | var name= userNode.getElementsByClassName("user_name")[0]; 162 | name.innerHTML= user.display_name; 163 | 164 | if (user.image) { 165 | image.src = user.image 166 | image.onerror= function(e) { 167 | e.currentTarget.src= "img/avatar.svg"; 168 | } 169 | } else { 170 | image.src = "img/avatar.svg"; 171 | } 172 | 173 | userNode.id= user.name; 174 | userNode.onclick= user_clicked; 175 | parent.appendChild(userNode); 176 | } 177 | setTimeout(show_users, 400); 178 | } 179 | 180 | function initialize_actions() { 181 | var template= document.getElementById("action_template"); 182 | var parent= template.parentElement; 183 | parent.removeChild(template); 184 | 185 | if (lightdm.can_suspend) { 186 | add_action("sleep","Sleep", "img/sleep.svg", function(e) {lightdm.suspend(); e.stopPropagation();}, template, parent); 187 | } 188 | if (lightdm.can_restart) { 189 | add_action("restart", "Restart", "img/restart.svg", function(e) {lightdm.restart(); e.stopPropagation();}, template, parent); 190 | } 191 | if (lightdm.can_shutdown) { 192 | add_action("shutdown", "Shutdown", "img/shutdown.svg", function(e) {lightdm.shutdown(); e.stopPropagation();}, template, parent); 193 | } 194 | } 195 | 196 | function initialize_timer() { 197 | update_time(); 198 | setInterval(update_time, 1000); 199 | } 200 | 201 | function add_action(id, name, image, clickhandler, template, parent) { 202 | action_node= template.cloneNode(true); 203 | action_node.id= "action_" + id; 204 | img_node= action_node.getElementsByClassName("action_image")[0]; 205 | label_node= action_node.getElementsByClassName("action_label")[0]; 206 | label_node.innerHTML= name; 207 | img_node.src= image; 208 | action_node.onclick= clickhandler; 209 | parent.appendChild(action_node); 210 | } 211 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-image: url('bg.jpg'); 3 | display: table; 4 | height: 100%; 5 | width: 100%; 6 | margin: 0; 7 | 8 | font-size: 10pt; 9 | text-shadow: 1px 1px 3px black; 10 | -webkit-user-select: none; 11 | } 12 | 13 | input { 14 | border: 1px solid white; 15 | border-radius: 4px; 16 | padding: 4px; 17 | box-shadow: 0 0 2px 1px white; 18 | 19 | -webkit-transition: box-shadow 0.3s ease-in-out; 20 | } 21 | 22 | input:focus { 23 | outline: none; 24 | box-shadow: 0 0 5px 2px #7DBEF1; 25 | } 26 | 27 | a { 28 | text-decoration: none; 29 | } 30 | 31 | .smooth { 32 | -webkit-transition: visibility 0s, opacity 0.3s, left 0.4s; 33 | } 34 | 35 | .hidden { 36 | opacity: 0; 37 | visibility: hidden; 38 | -webkit-transition-delay: 0.3s, 0s, 0s; 39 | } 40 | 41 | .center { 42 | text-align: center; 43 | } 44 | 45 | .button { 46 | cursor: pointer; 47 | } 48 | 49 | .header { 50 | height: 30%; 51 | } 52 | 53 | .footer { 54 | text-align: center; 55 | height: 100px; 56 | } 57 | 58 | .footer, .header { 59 | display: table-row; 60 | width: 100%; 61 | color: #B2B2B2; 62 | } 63 | 64 | .time { 65 | float: right; 66 | line-height: 25px; 67 | font-size: 11pt; 68 | margin-right: 10px; 69 | margin-top: 2px; 70 | } 71 | 72 | .login_content { 73 | display: table-row; 74 | } 75 | 76 | .login_container { 77 | display: table-cell; 78 | vertical-align: middle; 79 | } 80 | 81 | #message { 82 | display: table-row; 83 | height: 80px; 84 | -webkit-transition: visibility 0s, opacity 0.3s, height 0.3s; 85 | } 86 | 87 | #message.hidden { 88 | height: 0px; 89 | } 90 | 91 | #message_content { 92 | display: table-cell; 93 | vertical-align: top; 94 | text-align: center; 95 | color: #EFEFEF; 96 | } 97 | 98 | #message_content.error { 99 | color: #F55; 100 | } 101 | 102 | .user { 103 | display: inline-block; 104 | margin-left: 20px; 105 | margin-right: 20px; 106 | margin-bottom: 20px; 107 | position: relative; 108 | } 109 | 110 | .user:active { 111 | opacity: 0.5; 112 | } 113 | 114 | .user_image_wrapper { 115 | width: 80px; 116 | height: 80px; 117 | 118 | border-radius: 60px; 119 | box-shadow: 0 0 1px 1px #222, inset 0 0 2px 1px #222; 120 | border: 2px solid #EEE; 121 | background: -webkit-radial-gradient(circle, #FFF, #999); 122 | } 123 | 124 | .user_image_wrapper:hover { 125 | box-shadow: 0 0 1px 1px #222, inset 0 0 2px 1px #222, 0 0 5px 2px #7DBEF1; 126 | } 127 | 128 | .user_image { 129 | width: 80px; 130 | height: 80px; 131 | border-radius: 60px; 132 | } 133 | 134 | .user_name { 135 | display: block; 136 | margin-top: 15px; 137 | color: #EFEFEF; 138 | } 139 | 140 | .action { 141 | display: inline-block; 142 | width: 80px; 143 | margin-left: 40px; 144 | margin-right: 40px; 145 | } 146 | 147 | .action_image { 148 | height: 50px; 149 | width: 50px; 150 | 151 | margin-bottom: 5px; 152 | } 153 | 154 | .action_label { 155 | color: #B2B2B2; 156 | } 157 | --------------------------------------------------------------------------------