[Из песочницы] Dropclock для xscreensaver или как верстальщик писал заставку под Linux

Не помню, где первый раз увидел, но был очарован заставкой DropClock, о которой уже упоминалось на Хабре.0501d61b7856f7e47ed195e05e2b927e.png

Но вот беда: авторы собрали её только для Win и Mac. Несмотря на это, желание было сильнее ограничений, и я решил во что бы то ни стало собрать собственную реализацию.Первая мысль: попытаться реализовать программный рендер воды и брызги.Так как моё основное направление — веб, я начал искать реализации webGL и эффектов воды. В результате изысканий пришёл к WebGL Water.

Почитал немного документации, попробовал helloworld’ы, но это не моё.

Беглое чтение исходников и попытки правок не привнесли ясности в предмет исследований. Было принято решение посмотреть, а из чего же состоит оригинал.

Скачал с торрентов Win версию, на вайне не пошла, поставил на виртуалку.

На деле оказалось, что это флеш со вшитыми роликами: 10 с цифрами на чёрном фоне и 10 на белом, каждый длится минуту.Распаковщики swf их не потянули, но мне помог конвертер SWF to Video (win). Дальше уже в родной системе перекодировал в mp4/x264 ffmpeg-ом.

Далее дело оставалось за малым — заставить это работать в браузере. Для разнообразия ещё добавил подгрузку погоды с OpenWeatherMap.

Получилась вот такая разметка:

Стили (итоговые) html, body{height:100%; width:100%; margin:0; padding:0; overflow: hidden;} body{text-align: center; background-color:#000; color:#fff; font-family: Arial;} body.save{cursor: none;} .all{ width: 100%; max-width: 1375 px; height: calc (100% — 100 px); display: inline-block; white-space: nowrap; text-align: center; position: relative; padding: 0; margin:0 auto; } .d{ display: inline-block; line-height:0;/*width:312 px;*/max-width: calc ((100% — 115 px) / 4 — 2 px); height:100%; vertical-align: top; overflow: hidden; border:1 px solid #000;/*background:#fff;*/ } .d: after{display: block; width:100%; height:2 px; content:»; background:#000; margin-top:-2 px; position: relative; z-index:100;} .s{width:115 px; overflow: visible;} .d .sep{height:100%; animation: blick 1s ease infinite;} .r .sep{-webkit-animation: rotate 60s ease infinite;} .sep{position: relative; overflow: visible;} .sep: after,.sep: before{ position: absolute; top:40%; left:39%; z-index:1000; content:»; display: block; width:22%; height:20%; -webkit-animation: blick 1s ease infinite; /*background-color: red;*/ background-image: url («data: image/gif; base64, R0lGODlhAQABAIABAMzMzP///yH+EUNyZWF0ZWQgd2l0aCBHSU1QACwAAAAAAQABAAACAkQBADs=»); background-repeat: no-repeat; background-size: contain; } .sep: after{margin-top:94%;} .sep: before{} video{height:100%; max-height:700 px; width: calc (100% + 2 px); max-width:315 px; margin:-2 px -1 px 0; position: relative; z-index:50;} #info{/*display: none;*/text-align: right;} @-webkit-keyframes blick{ 0% {opacity:1;} 50% {opacity:.7;} 70% {opacity:.3;} 100% {opacity:1;} } @-webkit-keyframes rotate{ 0%{-webkit-transform: rotate (0deg);} 0.2%{ -webkit-transform: rotate (-30deg); } 0.8%{ -webkit-transform: rotate (390deg); } 1%{ -webkit-transform: rotate (360deg); } 100% {-webkit-transform: rotate (360deg);} } @keyframes blick{0% {opacity:1;}50% {opacity:.7;}70% {opacity:.3;}100% {opacity:1;}} .inspector{line-height:50 px; padding:0 25 px;} .time{float: right;} .weather{float: left;} .weather img{vertical-align: top;} .arrow{display: inline-block; width:20 px; height:50 px;}

@media screen and (min-height:775 px){ .all{ top: calc ((100% — 755 px) / 2); height: calc (100% — 202 px); } } @media screen and (max-height:775 px){ .all{ top:25 px; } } Скрипты (js и немного jquery) var digits = [0,0,0,0]; var msmove = timenow (); var city = 1485357; var lastrequesttime = 0; function timenow (){ return new Date * 1; } function getRandomArbitary (min, max){return Math.random () * (max — min) + min;} function extra0(d, e){ if (e===undefined)e=1; for (var i=0; i 2000) // if (!$('body').hasClass ('save')) $('body').addClass ('save'); // print info var info = document.getElementById ('info'); info.innerHTML = // timenow ()+' / '+ // msmove+' / '+ // flmm+' / '+ extra0(hors)+':'+extra0(mins)+':'+extra0(secs)+ // '.'+extra0(mili,2)+ ''; // replace for (var i in ndig){ if (ndig[i] != digits[i]){ if (lastrequesttime + getRandomArbitary (256,2048) < timenow()) updateDigit(i,ndig[i]); //document.getElementById('d'+i).src='b'+ndig[i]+'.mp4'; if(i==3){ updateWeather(); } } } setTimeout(step,getRandomArbitary(256,768)); } function updateDigit(i,n){ var d = $('#d'+i).clone().attr('src','dropclock_media/b'+n+'.mp4').appendTo('.d'+i); //setTimeout(function(){ // var i = d[0].dataset.d; $('#d'+i+':first').remove(); // },15000); //document.getElementById('d'+i).src='b'+n+'.mp4'; lastrequesttime = timenow(); digits[i] = n; } function updateWeather(){ // weather $.getJSON('http://api.openweathermap.org/data/2.5/weather?id='+city,function(data){ console.dir(data); var icon = 'http://openweathermap.org/img/w/'+data.weather[0].icon+'.png', temp = Math.floor(data.main.temp - 273.15), arrst= 'transform: rotate('+data.wind.deg+'deg);-webkit-transform: rotate('+data.wind.deg+'deg);', w_html = // 'Погода:'+ data.name+' '+ data.sys.country+' '+ ''+ data.weather[0].main+', '+ data.weather[0].description+' '+ ((temp>0)?'+':'')+temp+'° '+ '; Wind '+data.wind.speed+'m/s

'+ ''; $('.weather').html (w_html); }); }

$(function (){ // timer step (); $(window).mousemove (function (){ if (document.body.classList.contains ('save')) document.body.classList.remove ('save'); msmove = timenow (); }); });

В firefox видео притормаживало и дребезжало, поэтому изначально я был ориентирован на webkit.Добавил задержку падения цифр, чтобы не исчезали все сразу.Вроде работает, красиво, но нужно запускать это как самостоятельный файл.

Нашёл у себя в убунте системный мини-браузер webbrowser-app и, покапавшись в его параметрах, написал вот такой лаунчер:

#/bin/sh webbrowser-app --chromeless --fullscreen /var/www/vhosts/localhost/dropclock/index.html Но xscreensaver на него сильно ругался и я понял, что что-то делаю не так…Шло время, желание иметь красивую заставку никак не утолялось. 13 сентября, во всероссийский День Программиста, работа была отодвинута, а внимание снова сосредоточено на достижении призрачной цели. До этого видел оконные приложения на Python и выбор пал на него, как впоследствии оказалось, не зря. Раньше никогда ничего на нём не писал. Начал разбирать базовые примеры, добавил webkit.

Ура! Заработало! #!/usr/bin/env python # -*- coding: utf-8 -*-

import pygtk pygtk.require ('2.0') import os, argparse, gtk, webkit from gtk import gdk

class DropClock: def createParser (): self.parser = argparse.ArgumentParser () self.parser.add_argument ('-r', '--root', action='store_const', const=True, default=False) self.namespace = self.parser.parse_args ()

def delete_event (self, widget, event, data=None): print «Вызван сигнал удаления» return False def destroy (self, widget, data=None): print «Вызван сигнал уничтожения» gtk.main_quit ()

def __init__(self): self.parser = argparse.ArgumentParser () self.parser.add_argument ('-r', '--root', action='store_const', const=True, default=False) self.parser.add_argument ('-w', '-window-id', action='store_const', const=True, default='') self.namespace = self.parser.parse_args ()

url = 'file://' + os.path.realpath ('./index.html') self.window = gtk.Window (gtk.WINDOW_TOPLEVEL) self.window.set_default_size (1600, 768) self.window.set_position (gtk.WIN_POS_CENTER) self.window.connect («delete_event», self.delete_event) self.window.connect («destroy», self.destroy) self.browser = webkit.WebView () self.browser.props.settings.props.enable_default_context_menu = False self.browser.load_uri (url) self.window.add (self.browser) self.window.show_all () # if self.namespace.root: # self.window.fullscreen (); def main (self): gtk.main () # self.createParser ();

if __name__ == »__main__»: hello = DropClock () hello.main () А вдруг можно и к xscreensaver’у его прикрутить? Был найден пример заставки на питоне, на базе которого написан свой.Ничего сверхъестественного #!/usr/bin/python

import os import sys

import gtk, webkit from gtk import gdk

# the secret sauce is to get the «window id» out of $XSCREENSAVER_WINDOW # code comes from these two places: # 1) http://pastebin.com/nSCiq1P3 # 2) http://stackoverflow.com/questions/4598581/python-clutter-set-display

class ScreenSaverWindow (gtk.Window): def __init__(self): gtk.Window.__init__(self) pass

def delete_event (self, widget, event, data=None): return False def destroy (self, widget, data=None): gtk.main_quit ()

def realize (self): if self.flags () & gtk.REALIZED: return ident = os.environ.get ('XSCREENSAVER_WINDOW') if not ident is None: print 'if not ident is None:' self.window = gtk.gdk.window_foreign_new (int (ident, 16)) self.window.set_events (gdk.EXPOSURE_MASK | gdk.STRUCTURE_MASK) # added by aja x, y, w, h, depth = self.window.get_geometry () # self.size_allocate (gtk.gdk.Rectangle (x, y, w, h)) self.set_default_size (w, h) self.move (x, y) self.set_decorated (False) # aja — more self.window.set_user_data (self) self.style.attach (self.window) self.set_flags (self.flags () | gtk.REALIZED) #self.window.connect («destroy», self.destroy)

if self.window == None: print 'self.window == None:' self.window = gdk.Window (None, 1024, 768, gdk.WINDOW_TOPLEVEL,(gdk.EXPOSURE_MASK | gdk.STRUCTURE_MASK), gdk.INPUT_OUTPUT) # self.window.set_title («DropClock»)

if self.window!= None: print 'self.window!= None:' #self.window.add_filter (lambda *args: self.filter_event (args)) self.set_flags (self.flags () | gtk.REALIZED) self.browser = webkit.WebView () url = 'file://' + os.path.join (os.path.dirname (__file__) + '/index.html') self.browser.load_uri (url) self.add (self.browser) self.browser.show ()

window = ScreenSaverWindow () window.set_title ('DropClock') window.connect ('delete-event', gtk.main_quit) window.set_default_size (1024, 768) window.realize ()

window.modify_bg (gtk.STATE_NORMAL, gdk.color_parse («black»))

window.show ()

gtk.main () В ~/.xscreensaver под списком добавлена строка:- «Drop Clock» /var/www/vhosts/localhost/dropclock/dropclock_xss.py \n\Впоследствии ещё дорабатывал адаптивность стиля для корректного отображения в маленьком окне, получилось неплохо, мне нравится.Можно считать, что день программиста удался.Видео можно найти по магнет-ссылке.

© Habrahabr.ru