Hlavní navigace

Konfigurace Dwm – vzhled prostředí, systémový panel, úvod do patchů

Jaromír Vojtaj

Minulý díl byl věnován stylu rozložení oken, spouštěčům aplikací, pravidlům spouštění a ovládání prostředí myší. Dnešní díl se zaměří na vzhled, systémový panel a popis aplikace vybraného patche.

V minulém dílu jsem se v rámci konfigurace dostal až k akcím pomocí myších tlačítek a zbývá probrat už jenom tři věci. Dvě z nich budou velmi jednoduché, ta třetí je naopak poměrně komplikovaná. Začnu proto tou nejjednodušší záležitostí, na kterou je možné narazit hned na začátku konfiguračního souboru: celkový vzhled prostředí. Již dříve jsem zde zmínil nastavení fontů, takže jenom pro informaci přidávám ostatní nastavení vzhledu (ponechávám beze změny, nebudu ani nijak komentovat):

static const char normbordercolor[] = "#444444";
static const char normbgcolor[]     = "#222222";
static const char normfgcolor[]     = "#bbbbbb";
static const char selbordercolor[]  = "#005577";
static const char selbgcolor[]      = "#005577";
static const char selfgcolor[]      = "#eeeeee";
static const unsigned int borderpx  = 1;        /* border pixel of windows */
static const unsigned int snap      = 32;       /* snap pixel */

Další zatím neprobíranou vlastností Dwm je systémový panel. Už jsem se o něm samozřejmě zmínil v souvislosti s popisem tagů a stylů rozložení oken na ploše. Upřímně řečeno, o moc víc na něm také v základní verzi není… Za zmínku stojí ještě zobrazení názvu aktivního okna (skutečně pouze toho aktivního, bez ohledu na to, kolik oken je otevřeno celkem). Název je převzatý z parametru WM_NAME, o kterém byla řeč v minulém dílu při popisu pravidel pro spouštění aplikací/zobrazování oken. Více možná napoví jednoduchý obrázek

Název okna v panelu

Poslední součástí panelu, která byla také zmíněna v minulém dílu, je text v pravé části. Na první pohled to vypadá, že se jedná pouze o statický text. Ona to ale není úplně pravda. Jedná se o tzv. stavový panel, kde je možné zobrazovat různé uživatelské a hlavně užitečné informace. Bližší popis je možné najít třeba na ArchWiki: Nápověda Dwm. Z ní je jasná jedna věc: k tomu, aby bylo možné do stavového panelu cokoliv dostat, je nutné mít v systému nebo instalovat balíček xorg-xsetroot. Ten je v Manjaro Linuxu instalován v základní sadě balíčků, takže není nutné nic následně doinstalovávat. Je proto možné ihned vyzkoušet jednoduchou věc: do stavového panelu zapsat nějaký uživatelský text. K tomu stačí jenom doplnit do souboru $HOME/.xinitrc do příslušné sekce jeden řádek:

;;
dwm)
    xsetroot -name "Uživatelský text ve stavovém panelu"
    exec /usr/local/bin/dwm
;;

Po spuštění Dwm sezení vypadá panel tak, jak to ukazuje následující obrázek

Text ve stavovém panelu

Text se sice objevil, ale je asi jasné, že je to vhodné jenom a pouze na ukázkové účely. Ale vzhledem k tomu, že je do stavového panelu možné vložit prakticky libovolný text, je možné použít i jiné, mnohem praktičtější řešení. To je ostatně také k dispozici na zmíněné ArchWiki: do stavového panelu „promítnout“ výstup z aplikace Conky. To jsem demonstroval již při popisu Spectrwm, takže již je k dispozici konfigurační soubor. Nemá smysl ho pro daný účel nijak upravovat, takže nastane opět pouze změna v $HOME/.xinitrc:

(conky | while read LINE; do xsetroot -name "$LINE"; done) &;
exec /usr/local/bin/dwm

Výsledek jednoduché změny je vidět na obrázku

Conky ve stavovém panelu

Tím se dá říct, že je možné do stavového panelu dostat velké množství užitečných systémových informací a ještě si je konfigurovat a formátovat podle přání a potřeb uživatele. Nakonec ještě upozorním na dvě skutečnosti kolem panelu. Obě jsou v konfiguraci a první se týká zobrazení panelu a jeho umístění na ploše. Panel je možné umístit pouze nahoře nebo dole:

static const int showbar            = 1;        /* 0 means no bar */
static const int topbar             = 1;        /* 0 means bottom bar */

Druhá je pak klávesová zkratka, pomocí které je možné panel střídavě zobrazit a skrýt: Win+B

{ MOD,                   XK_b,      togglebar,      {0} },

Tímto by se dalo asi konstatovat, že jsem představil prakticky všechny možnosti a sekce konfiguračního souboru, které mají nějaký praktický smysl. Mohlo by se tedy zdát, že by mohla skončit i celá kapitola o Dwm. Z předchozího textu ale vyplývá, že to asi tak nebude, protože zbývá probrat poslední avizovanou záležitost. Tu upřesním v návaznosti na popis možností konfigurace panelu. Když použiju předchozí obrázek a podívám se na to, co na něm chybí, tak asi dojdu k následujícím závěrům:

  • v panelu nejsou žádné hodiny, datum, čili to, co je v něm běžně zobrazeno. V tomto případě není samozřejmě problém zobrazit datum pomocí Conky a do panelu ho tak doplnit
  • sice to nebylo nikde zmíněno, ale situace je podobná, jako byla ve Spectrwm – v panelu není k dispozici žádná systémová lišta, která by zobrazovala ikony aplikací, spuštěných na pozadí (např. správce schránky)
  • již bylo zmíněno, že v panelu se zobrazuje název pouze aktivního okna, tedy jednoho jediného, bez ohledu na celkový počet otevřených oken

Pokud bych se zaměřil nejenom na panel, tak by se různých „nedostatků“ dalo najít ještě mnohem víc. Tím se pomalu dostávám k tomu, že je Dwm možné upravit ještě i jiným způsobem, než je změna konfiguračního souboru. Tato možnost je dána skutečností, že je Dwm vytvořeno v programovacím jazyce C a je tvořeno dvěma základními soubory: zdrojovým dwm.c a hlavičkovým config.h. Je asi jasné (a trochu podobné třeba Qtile), že se zde nabízí cesta ke změnám. Tyto změny samozřejmě obecně spočívají v doplnění funkcí a procedur do zdrojového souboru. Je to řečeno velmi jednoduše, ale tak jednoduché to zase není. Aby to mohl uživatel udělat, musel by celkem slušně ovládat jazyk C a také proniknout do „útrob“ zdrojového kódu Dwm a něco měnit či upravovat. Toto je samozřejmě možné a proveditelné, ale naštěstí existuje i trochu jednodušší cesta. Tou je použití tzv. patches (záplaty, přílepky).

Obecně je možné říct, že použití patchů je v programování celkem běžné a je tak logické, že je lze použít i v rámci změn v Dwm. Velká výhoda spočívá v tom, že jsou již mnohé patche pro Dwm připravené a stačí je jenom použít. Ono se to sice snáze řekne (nebo v tomto případě napíše), než provede, ale je to pořád lepší, než něco vymýšlet „na zelené louce“. Přímo na stránkách Dwm je k dipozici docela slušné množství patchů (něco přes 80): Dwm patches. V rámci každého z nich je k dispozici krátký popis jeho funkce a odkaz na stažení příslušného souboru. Než se ale pustím do přímé aplikace patchů, je nutné si říct něco málo o jejich teoretickém fungování.

Princip patchování spočívá v tom, že existují dva soubory. První z nich je originální (původní, předchozí verze atd.) soubor a druhý tzv. rozdílový soubor. Při použití patche se děje to, že na určité místo v originálním souboru (to je přesně určené v souboru rozdílovém) se buď přidávají celé řádky kódu nebo se naopak odebírají. To tedy znamená, že když se požaduje změna jednoho znaku v celém řádku, tak je nutné původní řádek odstranit a jeho novou verzi následně přidat. To je asi ve stručnosti princip aplikace patchů. Pro jejich použití existují v Linuxu i jiných OS standardní nástroje, protože se jedná o běžnou součást vývojářské práce. Abych ale nepsal jenom čistě teoreticky, tak jsem si vybral jeden patch, na kterém budu demonstrovat více. Jeho stránka je vidět na obrázku

Patch Focusonclick

Jak je z obrázku patrné, jedná se o změnu při přenosu fokusu mezi otevřenými okny. Ve standardním stavu se fokus přenáší pouze přesunem kurzoru myši na vybrané okno aplikace. Tento patch standardní chování změní tak, že pro přenos fokusu je nutné kliknutí myši ve vybraném okně aplikace. Sám za sebe mohu říct, že toto nepoužívám a myslím si, že to pro dlaždicové WM není vhodný způsob, ale někdo může změnu ocenit. A hlavně: tento patch je krátký a jednoduchý a je na něm možné demonstrovat použití a také samozřejmě záludnosti a případné zádrhele. Když se ještě vrátím k obrázku, tak je kromě popisu funkce patche k dispozici možnost stažení rozdílového souboru (obvykle více verzí) a informace o autorovi/autorech.

Ukázku tedy zahájím tím, že si stáhnu příslušný rozdílový soubor, konkrétně ten první v pořadí, tedy novější. Jak je z názvu souboru zřejmé, má příponu, která se odkazuje nejenom na něco společného s rozdílem, ale je pro takové typy souborů standardně určena. Následně je nutné ho dostat do adresáře, kde jsou všechny soubory Dwm:

cp $HOME/dwm-focusonclick-6.0.diff $HOME/Dokumenty/dwm-6.1/focusonclick.diff

Než se dostanu k aplikaci vybraného patche, bude nutné se mu podívat trochu „pod kapotu“. Zde je prvních pár řádků z celkového počtu 48:

diff -up dwm-6.0/config.def.h dwm-6.0-focusonclick/config.def.h
--- dwm-6.0/config.def.h    2011-12-19 09:02:46.000000000 -0600
+++ dwm-6.0-focusonclick/config.def.h    2012-11-24 17:48:19.867072611 -0600
@@ -13,6 +13,9 @@ static const unsigned int snap      = 32
 static const Bool showbar           = True;     /* False means no bar */
 static const Bool topbar            = True;     /* False means bottom bar */

+/* False means using the scroll wheel on a window will not change focus */
+static const Bool focusonwheelscroll = False;
+
 /* tagging */
 static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };

diff -up dwm-6.0/dwm.c dwm-6.0-focusonclick/dwm.c
--- dwm-6.0/dwm.c    2011-12-19 09:02:46.000000000 -0600
+++ dwm-6.0-focusonclick/dwm.c    2012-11-24 18:00:08.902254102 -0600
@@ -266,7 +266,6 @@ static void (*handler[LASTEvent]) (XEven

Jenom na okraj: podobné soubory je velmi vhodné otevírat v nějakém nástroji, který je schopen rozlišovat různé typy souborů a hlavně v nich obsažených klíčových slov. Já na tyto účely používám Geany a tak budu soubor popisovat včetně barevného odlišení v této aplikaci. Další upozornění: všechny patche (dokonce ani ty přímo z webu Dwm) nemusejí mít vždy úplně stejnou strukturu!

  • první řádek (modrý) – začíná klíčovým slovem diff a uvádí, který soubor bude považován za originál. V tomto případě se bude změna týkat hlavičkového konfiguračního souboru
  • druhý a třetí řádek (šedozelený) – informace z doby vytvoření patche (nebudu podrobněji rozebírat) na originálním a upraveném souboru
  • čtvrtý řádek (zelený) – první z tzv. HUNKS. To jsou vlastně jednotlivé výkonné příkazy, které upravují originální soubor a tvoří z něj novou verzi. Jejich obecný tvar je @@ -R,r +R,r @@ kód   

Značka -R označuje počáteční řádek kódu v originálním souboru, kterého se změna týká. Značka +R označuje totéž v nové verzi souboru.

Značky -r/+r ukazují počet řádků, kterých se změna dotkne v originálním a novém souboru. Zde to konkrétně znamená, že v novém souboru přibudou 3 řádky. Kdyby řádků naopak ubylo, bylo by druhé číslo menší (viz poslední řádek s hlavičkou druhého hunku)

Za druhou značkou @@ je pak zobrazen kód, který je očekáván na uvedeném řádku v originálním souboru a tvoří tak počáteční bod, kde bude začínat změna

  • pátý až sedmý řádek (černý)- první trojice šesti dotčených řádků v originálním souboru. Důležitá informace: tyto řádky NEMUSEJÍ začínat bezprostředně po hlavičce hunku. Zde je maximální snaha, aby hlavička hunku odkazovala na začátek nějaké funkce či procedury a ne do nějakého „běžného“ kódu. Každopádně se ale první z trojice řádků nachází na místě (čísle řádku), které je uvedeno v hlavičce hunku
  • osmý až desátý řádek (světle zelený) – nové tři řádky, přidané do originálního souboru včetně prázdného řádku. na začátku řádky pro přidání je znak PLUS(+), pro odstranění řádku pak znak MINUS(-). Řádky pro odstranění mají v Geany červenou barvu
  • řádky 11 – 13 (černé) – následující tři řádky z originálního souboru, které musí být po změně na tomto místě obsažené i v nové verzi souboru
  • řádek 14 (modrý)- uvození změn (jedné nebo více) v dalším originálním souboru, tentokrát přímo ve zdrojovém
  • řádky 15 – 16 (šedozelené) – informace o druhém upravovaném souboru, opět pořadí originál/nová verze
  • řádek 17 (zelený) – první hunk nového souboru
  • atd.

Pokud by měl někdo zájem blíže tuto problematiku studovat, jistě najde dostatek podkladů. Já jenom pro ještě podrobnější objasnění struktury rozdílového souboru odkážu na jednu možnost: Formát diff souboru. Navíc dávám do přílohy kompletní text rozdílového souboru: Rozdílový soubor Focusonclick.

Po základním objasnění struktury rozdílového souboru by asi bylo vhodné se podívat na to, co se při jeho aplikaci vlastně děje. Zatím na to nemá smysl použít nějaký příkaz, takže se zkusím podívat na „ruční“ možnosti. První hunk říká, že se budou přidávat tři nové řádky s tím, že start hunku je na řádku č. 13. A už zde je možné narazit na zákeřný problém! Stačí se podívat na úvodní řádky původního konfiguračního souboru a toho, který vznikl postupnými úpravami. Rozdíl je už na čtvrtém řádku:

static const char *fonts[] = {
    "monospace:size=10"
};
static const char dmenufont[] = "monospace:size=10";

vs

static const char *fonts[]         = {"monospace:bold:size=10"};
static const char dmenufont[]   = "monospace:bold:size=11";

Jak je z výpisů patrné, v původní konfiguraci zabírají uvedené dva příkazy čtyři řádky, v upravené verzi pouze dva. Když se zaměřím na to, co by mělo na řádku č. 13 být, tak v rozdílovém souboru je to kód

static const unsigned int snap      = 32

Pokud se znovu podívám na původní konfiguraci, tak je tento kód až na řádku č. 15. V upravené verzi souboru je správně na řádku č. 13. Z poměrně podrobného popisu je snad každému jasné, že spuštění a úspěšné dokončení patche může být celkem dobrodružnou záležitostí a ne vždy úplně úspěšnou!!! Já na dvou jednoduchých příkladech ukážu, co se stane, když se projeví nějaká komplikace. Abych nepřišel o dosavadní konfiguraci, tak si stávající soubory uložím pod jiným názvem (v adresáři $HOME/Dokumenty/dwm-6.1):

cp config.h config.orig.h
cp dwm.c dwm.orig.c
cp config.def.h config.h

Jak je z předchozích příkazů jasné, tak se pokusím uplatnit patch na původní konfigurační a zdrojový soubor, i když je asi všem jasné, že to nemá šanci na úspěch! Ale nevadí, špatný výsledek je také výsledek… Abych to ale úplně nedramatizoval, tak zkusím nepoužít „ostrý“ příkaz pro aplikaci patche, ale jenom jeho provedení „nasucho“:

patch --dry-run -p1 < focusonclick.diff

vs

patch -p1 < focusonclick.diff

Výsledek je viditelný na obrázku

Aplikace patche č. 1

Nebudu zatím výsledky nijak rozebírat, jenom je nutné si všimnout, že byl patch aplikován na soubor config.def.h, což není úplně žádoucí. Je proto potřeba rozdílový soubor otevřít a v prvních třech řádcích provést změny, které odkážou na aplikaci do souboru config.h:

diff -up dwm-6.0/config.h dwm-6.0-focusonclick/config.h
--- dwm-6.0/config.h    2011-12-19 09:02:46.000000000 -0600
+++ dwm-6.0-focusonclick/config.h    2012-11-24 17:48:19.867072611 -0600

Po uložení změn a nové zkoušce aplikace patche je výsledek již o něco lepší – viz obrázek

Aplikace patche č. 2

Až teď je potřeba si všimnout, že u předpokládaného problému s číslem řádku v konfiguračním souboru se hunk tváří, že je vše v pořádku. Vzhledem k tomu, že mám vytvořené zálohy důležitých souborů, je možné zkusit provést aplikaci patche „naostro“. Výpis příkazu je vidět na obrázku

Aplikace patche č. 3

Kromě již dříve známých hlášení se zde objevilo jedno další: odmítnuté/neprovedené hunky byly uloženy na souboru dwm.c.rej. Jeho obsah je vidět na obrázku

Soubor s neprovedenými hunky

Celkový výsledek je tedy takový, že jeden hunk v konfiguračním souboru byl proveden, stejně tak jeden ve zdrojovém souboru. Naopak dva hunky ve zdrojovém souboru provedeny nebyly. Zkusím se tedy podívat na konfigurační soubor, kde by podle všech předpokladů neměla být aplikace patche úspěšná. Jak ale ukazuje další obrázek, tak je správný kód na správném místě.

Nová verze konfiguračního souboru

Ještě než budu dělat nějaké zásadní závěry, tak zkusím aplikovat patch na aktuální konfigurační soubor, kde by podle předpokladu mělo být vše v pořádku včetně správného čísla řádku pro začátek změny. Podle očekávání je i zde vše v pořádku a nové tři řádky „sedí“ na svých předpokládaných místech. A teď konečně ten závěr: při aplikaci patchů je důležitější informace o kódu před a za změněnými řádky než přesná čísla řádků uvedená v rozdílovém souboru. To je nakonec velmi dobře patrné na úspěšném hunku ve zdrojovém souboru. Ten měl pouze jedinou funkci: na řádku 266 najít hlavičku procedury static void (*handler[LASTEvent]) (XEven a v následujícím kódu odstranit řádek:

[ConfigureRequest] = configurerequest,
[ConfigureNotify] = configurenotify,
[DestroyNotify] = destroynotify,
-    [EnterNotify] = enternotify,[Expose] = expose,
[FocusIn] = focusin,
[KeyPress] = keypress,

Když se podívám do souboru dwm.c, kde byla tato změna opravdu provedena, tak se potvrdí předchozí závěr. Začátek procedury je v souboru sice na řádku č. 246 (v rozdílovém souboru je „požadavek“ na řádek 266), ale požadovaný řádek je řádně odstraněn. Ještě víc předchozí závěr dokazuje skutečnost, kterou jsem explicitně nezdůraznil: já už vlastně nemám originální zdrojový soubor ze stránek projektu, protože jsem do něj již v předchozích dílech přidal jeden řádek v rámci konfigurace klávesových zkratek! Dokladem úspěšné aplikace patche je obrázek

Změna ve zdrojovém souboru

Nyní se vrátím k nepodařeným hunkům. V nich se pátralo po funkcích

@@ -455,7 +454,9 @@ buttonpress(XEvent *e) {
@@ -1001,11 +1002,11 @@ grabbuttons(Client *c, Bool focused) {

Pokud se podívám na příslušné řádky a jejich okolí, tak se mi nepodaří hledané funkce ani následný kód objevit. Půjdu tedy ještě dál a nechám prohledat celý zdrojový soubor. A výsledek: ani jeden z požadovaných kódů zde není na správném místě (místo toho na řádcích 417 a 943)! Tím narážím na další možnou problematiku patchů: různé verze patche a originálního souboru. Z předchozích ukázek kódu vyplývá, že patch je určen pro Dwm verze 6.0 a já mám aktuální verzi 6.1. Pak je asi logický závěr, že nová verze zdrojového souboru již může obsahovat jiný kód, než verze předchozí. Nebo alespoň jako v konkrétním příkladě je hledaný kód úplně někde jinde. Ono to mohlo být minimálně podezřelé již na začátku, ale pro demonstrační účely bylo vhodné „odhalení“ ponechat až na konec… Bylo by samozřejmě možné hlouběji analyzovat kód zdrojového souboru a pokusit se patch nějak aplikovat „ručně“. V daném případě by to nebylo nijak složité, ale nebudu se tím už v dnešním dílu zabývat. Více o aplikaci patchů, a to již mnohem užitečnějších, si nechám na díl příští.

Dnešní díl jsem věnoval krátkému popisu nastavení vzhledu prostředí a možnostem konfigurace systémového panelu s důrazem na panel stavový. Hlavní část dílu pak byla věnována popisu možnosti aplikace patchů na konfigurační i zdrojový soubor. Součástí byl podrobnější popis principu aplikace patchů a ukázka na jednom vybraném a jednoduchém vzorku. V příštím dílu se pustím do podrobnějšího popisu aplikace několika vybraných patchů a upozorním na další, které mohou být pro uživatele zajímavé. Díl a kapitola o Dwm pak budou ukončeny přehledem všech aktuálních verzí důležitých souborů.

Našli jste v článku chybu?

20. 10. 2016 9:05

Palo (neregistrovaný)

Prosim vas kto prisiel s tym skvelym napadom zakompilovat konfiguraciu do kodu? To akoze ked kazdy uzivatel na systeme bude chciet iny vzhlad tak tam bude pre kazdeho extra binarka? To je nejako narocne na zaciatku nacitat nejaky textovy konfiguracny subor? Viem ze ma tu zacnete bombardovat ale ja som to naozaj nepochopil. U niektorych programov to beriem akoze autor nemal cas ale prehlasit to za ficuru to je moc.

22. 10. 2016 1:54

FrostyX (neregistrovaný)

> To je nejako narocne na zaciatku nacitat nejaky textovy konfiguracny subor?

Není to náročné, ale pak by si uživatel takové prostředí mohl nastavit jen pomocí předem daných možností. Tím, že se prostředí konfiguruje přímo ve zdrojovém kódu, nemá uživatel svázané ruce a může si dělat co chce.

Potřeba kompilace dwm je daná tím, že je napsané v C. Pokud bys chtěl možnosti jako u dwm, ale chtěl se vyhnout kompilaci, můžeš zkusit třeba Qtile (python), nebo Awesome (lua), etc.

Měšec.cz: Air Bank zruší TOP3 garanci a zdražuje kurzy

Air Bank zruší TOP3 garanci a zdražuje kurzy

Vitalia.cz: Taky věříte na pravidlo 5 sekund?

Taky věříte na pravidlo 5 sekund?

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?

Lupa.cz: Kdo pochopí vtip, může jít do ČT vyvíjet weby

Kdo pochopí vtip, může jít do ČT vyvíjet weby

Podnikatel.cz: Změny v cestovních náhradách 2017

Změny v cestovních náhradách 2017

Lupa.cz: Insolvenční řízení kvůli cookies? Vítejte v ČR

Insolvenční řízení kvůli cookies? Vítejte v ČR

Podnikatel.cz: Chaos u EET pokračuje. Jsou tu další návrhy

Chaos u EET pokračuje. Jsou tu další návrhy

Vitalia.cz: Mondelez stahuje rizikovou čokoládu Milka

Mondelez stahuje rizikovou čokoládu Milka

Vitalia.cz: Když přijdete o oko, přijdete na rok o řidičák

Když přijdete o oko, přijdete na rok o řidičák

120na80.cz: Horní cesty dýchací. Zkuste fytofarmaka

Horní cesty dýchací. Zkuste fytofarmaka

Vitalia.cz: Pamlsková vyhláška bude platit jen na základkách

Pamlsková vyhláška bude platit jen na základkách

Vitalia.cz: Proč vás každý zubař posílá na dentální hygienu

Proč vás každý zubař posílá na dentální hygienu

Měšec.cz: U levneELEKTRO.cz už reklamaci nevyřídíte

U levneELEKTRO.cz už reklamaci nevyřídíte

Měšec.cz: mBank cenzuruje, zrušila mFórum

mBank cenzuruje, zrušila mFórum

DigiZone.cz: Rádio Šlágr má licenci pro digi vysílání

Rádio Šlágr má licenci pro digi vysílání

Podnikatel.cz: Udávání a účtenková loterie, hloupá komedie

Udávání a účtenková loterie, hloupá komedie

Měšec.cz: Jak levně odeslat balík přímo z domu?

Jak levně odeslat balík přímo z domu?

Vitalia.cz: Jsou čajové sáčky toxické?

Jsou čajové sáčky toxické?

120na80.cz: Co všechno ovlivňuje ženskou plodnost?

Co všechno ovlivňuje ženskou plodnost?

Podnikatel.cz: Udávání kvůli EET začalo

Udávání kvůli EET začalo