news download themes documentation links










Ewmh.cc

00001 // Ewmh.cc for fluxbox
00002 // Copyright (c) 2002-2003 Henrik Kinnunen (fluxgen at user.sourceforge.net)
00003 //
00004 // Permission is hereby granted, free of charge, to any person obtaining a
00005 // copy of this software and associated documentation files (the "Software"),
00006 // to deal in the Software without restriction, including without limitation
00007 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
00008 // and/or sell copies of the Software, and to permit persons to whom the
00009 // Software is furnished to do so, subject to the following conditions:
00010 //
00011 // The above copyright notice and this permission notice shall be included in
00012 // all copies or substantial portions of the Software.
00013 //
00014 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
00017 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00018 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00019 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00020 // DEALINGS IN THE SOFTWARE.
00021 
00022 // $Id: Ewmh.cc,v 1.36 2003/12/19 00:36:53 fluxgen Exp $
00023 
00024 #include "Ewmh.hh" 
00025 
00026 #include "Screen.hh"
00027 #include "Window.hh"
00028 #include "WinClient.hh"
00029 #include "Workspace.hh"
00030 #include "fluxbox.hh"
00031 
00032 #include <iostream>
00033 #include <algorithm>
00034 #include <new>
00035 using namespace std;
00036 
00037 Ewmh::Ewmh() {
00038     createAtoms();
00039     enableUpdate();
00040 }
00041 
00042 Ewmh::~Ewmh() {
00043     while (!m_windows.empty()) {
00044         XDestroyWindow(FbTk::App::instance()->display(), m_windows.back());
00045         m_windows.pop_back();
00046     }
00047 }
00048 
00049 void Ewmh::initForScreen(BScreen &screen) {
00050     Display *disp = FbTk::App::instance()->display();
00051 
00052 
00053     Window wincheck = XCreateSimpleWindow(disp,
00054                                           screen.rootWindow().window(), 
00055                                           0, 0, 5, 5, 0, 0, 0);
00056 
00057     if (wincheck != None) {
00058         // store the window so we can delete it later
00059         m_windows.push_back(wincheck);
00060         
00061         screen.rootWindow().changeProperty(m_net_supporting_wm_check, XA_WINDOW, 32,
00062                                            PropModeReplace, (unsigned char *) &wincheck, 1);
00063         XChangeProperty(disp, wincheck, m_net_supporting_wm_check, XA_WINDOW, 32,
00064             PropModeReplace, (unsigned char *) &wincheck, 1);
00065 
00066         XChangeProperty(disp, wincheck, m_net_wm_name, XA_STRING, 8,
00067             PropModeReplace, (unsigned char *) "Fluxbox", strlen("Fluxbox"));
00068     }
00069     
00070     //set supported atoms
00071     Atom atomsupported[] = {
00072         // window properties
00073         m_net_wm_strut,
00074         m_net_wm_state,
00075         // states that we support:
00076         m_net_wm_state_sticky,
00077         m_net_wm_state_shaded,
00078     m_net_wm_state_maximized_horz,
00079     m_net_wm_state_maximized_vert,
00080     m_net_wm_state_fullscreen,
00081 
00082         m_net_wm_desktop,
00083 
00084         // root properties
00085         m_net_client_list,
00086         m_net_number_of_desktops,
00087         m_net_current_desktop,
00088         m_net_active_window,
00089         m_net_close_window,
00090         m_net_moveresize_window,
00091         m_net_desktop_names,
00092         m_net_supporting_wm_check       
00093     };
00094 
00095     screen.rootWindow().changeProperty(m_net_supported, XA_ATOM, 32,
00096                                        PropModeReplace, 
00097                                        (unsigned char *) &atomsupported, 
00098                                        (sizeof atomsupported)/sizeof atomsupported[0]);
00099 
00100     
00101 }
00102 
00103 void Ewmh::setupClient(WinClient &winclient) {
00104     updateStrut(winclient);
00105 }
00106 
00107 void Ewmh::setupFrame(FluxboxWindow &win) {
00108 
00109     Atom ret_type;
00110     int fmt;
00111     unsigned long nitems, bytes_after;
00112     long *data = 0;
00113 /*  
00114     if (XGetWindowProperty(disp, win.clientWindow(), 
00115         m_net_wm_state, 0, 1, False, XA_CARDINAL, 
00116         &ret_type, &fmt, &nitems, &bytes_after, 
00117         (unsigned char **) &data) ==  Success && data) {
00118         flags = *data;
00119         setState(win, flags);
00120         XFree(data);
00121     }
00122 */
00123     if (win.winClient().property(m_net_wm_desktop, 0, 1, False, XA_CARDINAL, 
00124                                  &ret_type, &fmt, &nitems, &bytes_after, 
00125                                  (unsigned char **) &data) && data) {
00126         unsigned int desktop = static_cast<unsigned int>(*data);
00127         if (desktop == 0xFFFFFFFF && !win.isStuck())
00128             win.stick();
00129         else
00130             win.screen().sendToWorkspace(desktop, &win, false);
00131 
00132         XFree(data);
00133     }
00134 
00135 }
00136 
00137 void Ewmh::updateFrameClose(FluxboxWindow &win) {
00138     clearState(win);
00139 }
00140 
00141 void Ewmh::updateClientList(BScreen &screen) {
00142     size_t num=0;
00143 
00144     BScreen::Workspaces::const_iterator workspace_it = 
00145         screen.getWorkspacesList().begin();
00146     BScreen::Workspaces::const_iterator workspace_it_end = 
00147         screen.getWorkspacesList().end();
00148     for (; workspace_it != workspace_it_end; ++workspace_it) {
00149         Workspace::Windows::iterator win_it = 
00150             (*workspace_it)->windowList().begin();
00151         Workspace::Windows::iterator win_it_end = 
00152             (*workspace_it)->windowList().end();
00153         for (; win_it != win_it_end; ++win_it) {
00154             num += (*win_it)->numClients();
00155         }
00156 
00157     }
00158     // and count icons
00159     BScreen::Icons::const_iterator icon_it = screen.getIconList().begin();
00160     BScreen::Icons::const_iterator icon_it_end = screen.getIconList().end();        
00161     for (; icon_it != icon_it_end; ++icon_it) {
00162         num += (*icon_it)->numClients();
00163     }
00164     
00165     Window *wl = new (nothrow) Window[num];
00166     if (wl == 0) {
00167         cerr<<"Fatal: Out of memory, can't allocate for Ewmh client list"<<endl;
00168         return;
00169     }
00170 
00171     //start the iterator from begining
00172     workspace_it = screen.getWorkspacesList().begin();
00173     int win=0;
00174     for (; workspace_it != workspace_it_end; ++workspace_it) {
00175     
00176         // Fill in array of window ID's
00177         Workspace::Windows::const_iterator it = 
00178             (*workspace_it)->windowList().begin();
00179         Workspace::Windows::const_iterator it_end = 
00180             (*workspace_it)->windowList().end();        
00181         for (; it != it_end; ++it) {
00182             if ((*it)->numClients() == 1)
00183                 wl[win++] = (*it)->clientWindow();
00184             else {
00185                 // add every client in fluxboxwindow to list window list
00186                 std::list<WinClient *>::iterator client_it = 
00187                     (*it)->clientList().begin();
00188                 std::list<WinClient *>::iterator client_it_end = 
00189                     (*it)->clientList().end();
00190                 for (; client_it != client_it_end; ++client_it)
00191                     wl[win++] = (*client_it)->window();
00192             }
00193         }
00194     }
00195 
00196     // plus iconified windows
00197     icon_it = screen.getIconList().begin();
00198     for (; icon_it != icon_it_end; ++icon_it) {
00199         FluxboxWindow::ClientList::iterator client_it = (*icon_it)->clientList().begin();
00200         FluxboxWindow::ClientList::iterator client_it_end = (*icon_it)->clientList().end();
00201         for (; client_it != client_it_end; ++client_it)
00202             wl[win++] = (*client_it)->window();
00203     }
00204     
00205     //number of windows to show in client list
00206     num = win;
00207     screen.rootWindow().changeProperty(m_net_client_list,
00208                                        XA_CARDINAL, 32,
00209                                        PropModeReplace, (unsigned char *)wl, num);
00210     
00211     delete [] wl;
00212 }
00213 
00214 void Ewmh::updateWorkspaceNames(BScreen &screen) {
00215     XTextProperty text;
00216     const BScreen::WorkspaceNames &workspacenames = screen.getWorkspaceNames();
00217     const size_t number_of_desks = workspacenames.size();
00218     
00219     char *names[number_of_desks];       
00220     
00221     for (size_t i = 0; i < number_of_desks; i++) {      
00222         names[i] = new char[workspacenames[i].size() + 1]; // +1 for \0
00223         memset(names[i], 0, workspacenames[i].size());
00224         strcpy(names[i], workspacenames[i].c_str());
00225     }
00226 
00227     if (XStringListToTextProperty(names, number_of_desks, &text)) {
00228         XSetTextProperty(FbTk::App::instance()->display(), screen.rootWindow().window(),
00229              &text, m_net_desktop_names);
00230         XFree(text.value);
00231     }
00232     
00233     for (size_t i = 0; i < number_of_desks; i++)
00234         delete [] names[i]; 
00235 }
00236 
00237 void Ewmh::updateCurrentWorkspace(BScreen &screen) {
00238     size_t workspace = screen.currentWorkspaceID();
00239     screen.rootWindow().changeProperty(m_net_current_desktop, XA_CARDINAL, 32, PropModeReplace,
00240                                        (unsigned char *)&workspace, 1);
00241 
00242 }
00243 
00244 void Ewmh::updateWorkspaceCount(BScreen &screen) {
00245     size_t numworkspaces = screen.getCount();
00246     screen.rootWindow().changeProperty(m_net_number_of_desktops, XA_CARDINAL, 32, PropModeReplace,
00247                                        (unsigned char *)&numworkspaces, 1);
00248 }
00249 
00250 void Ewmh::updateState(FluxboxWindow &win) {
00252 }
00253 
00254 void Ewmh::updateLayer(FluxboxWindow &win) {
00256 }
00257 
00258 void Ewmh::updateHints(FluxboxWindow &win) {
00259 }
00260 
00261 void Ewmh::updateWorkspace(FluxboxWindow &win) {
00262     int workspace = win.workspaceNumber();
00263     if (win.isStuck())
00264         workspace = 0xFFFFFFFF; // appear on all desktops/workspaces
00265 
00266     FluxboxWindow::ClientList::iterator it = win.clientList().begin();
00267     FluxboxWindow::ClientList::iterator it_end = win.clientList().end();
00268     for (; it != it_end; ++it) {
00269         (*it)->changeProperty(m_net_wm_desktop, XA_CARDINAL, 32, PropModeReplace,
00270                               (unsigned char *)&workspace, 1);
00271     }
00272 
00273 }
00274 
00275 // return true if we did handle the atom here
00276 bool Ewmh::checkClientMessage(const XClientMessageEvent &ce, BScreen * screen, WinClient * const winclient) {
00277 
00278     if (ce.message_type == m_net_wm_desktop) {
00279         if (screen == 0)
00280             return true;
00281         // ce.data.l[0] = workspace number
00282         // valid window and workspace number?
00283         if (winclient == 0 || winclient->fbwindow() == 0 ||
00284             static_cast<unsigned int>(ce.data.l[0]) >= screen->getCount())
00285             return true;
00286         
00287         screen->sendToWorkspace(ce.data.l[0], winclient->fbwindow(), false);        
00288         return true;
00289     } else if (ce.message_type == m_net_wm_state) {
00290         if (winclient == 0 || winclient->fbwindow() == 0)
00291             return true;
00292 
00293         FluxboxWindow &win = *winclient->fbwindow();
00294         // ce.data.l[0] = the action (remove, add or toggle)
00295         // ce.data.l[1] = the first property to alter
00296         // ce.data.l[2] = second property to alter (can be zero)
00297         if (ce.data.l[0] == STATE_REMOVE) {
00298             setState(win, ce.data.l[1], false);
00299             setState(win, ce.data.l[2], false);
00300         } else if (ce.data.l[0] == STATE_ADD) {
00301             setState(win, ce.data.l[1], true);
00302             setState(win, ce.data.l[2], true);
00303         } else if (ce.data.l[0] == STATE_TOGGLE) {
00304             toggleState(win, ce.data.l[1]);
00305             toggleState(win, ce.data.l[2]);
00306         }
00307 
00308         return true;
00309     } else if (ce.message_type == m_net_number_of_desktops) {
00310         if (screen == 0)
00311             return true;
00312         // ce.data.l[0] = number of workspaces
00313         
00314         // no need to alter number of desktops if they are the same
00315         // or if requested number of workspace is less than zero
00316         if (screen->getCount() == static_cast<unsigned int>(ce.data.l[0]) || 
00317             ce.data.l[0] < 0)
00318             return true;
00319 
00320         if (screen->getCount() > static_cast<unsigned int>(ce.data.l[0])) {
00321             // remove last workspace until we have
00322             // the same number of workspaces
00323             while (screen->getCount() != static_cast<unsigned int>(ce.data.l[0])) {
00324                 screen->removeLastWorkspace();
00325                 if (screen->getCount() == 1) // must have at least one workspace
00326                     break;
00327             }
00328         } else { // add workspaces to screen until workspace count match the requested size
00329             while (screen->getCount() != static_cast<unsigned int>(ce.data.l[0])) {
00330                 screen->addWorkspace();                 
00331             }
00332         }
00333             
00334         return true;
00335     } else if (ce.message_type == m_net_current_desktop) {
00336         if (screen == 0)
00337             return true;
00338         // ce.data.l[0] = workspace number
00339         
00340         // prevent out of range value
00341         if (static_cast<unsigned int>(ce.data.l[0]) >= screen->getCount())
00342             return true;
00343         screen->changeWorkspaceID(ce.data.l[0]);
00344         return true;
00345     } else if (ce.message_type == m_net_active_window) {
00346         
00347         // make sure we have a valid window
00348         if (winclient == 0)
00349             return true;
00350         // ce.window = window to focus
00351         
00352         winclient->focus();
00353         return true;
00354     } else if (ce.message_type == m_net_close_window) {
00355         if (winclient == 0)
00356             return true;
00357         // ce.window = window to close (which in this case is the win argument)
00358         winclient->sendClose();
00359         return true;
00360     } else if (ce.message_type == m_net_moveresize_window) {
00361         if (winclient == 0 && winclient->fbwindow())
00362             return true;
00363         // ce.data.l[0] = gravity and flags
00364         // ce.data.l[1] = x
00365         // ce.data.l[2] = y
00366         // ce.data.l[3] = width
00367         // ce.data.l[4] = height
00368         // TODO: gravity and flags
00369         winclient->fbwindow()->moveResize(ce.data.l[1], ce.data.l[2],
00370                        ce.data.l[3], ce.data.l[4]);
00371         return true;
00372     }
00373 
00374     // we didn't handle the ce.message_type here
00375     return false;
00376 }
00377 
00378 
00379 bool Ewmh::propertyNotify(WinClient &winclient, Atom the_property) {
00380     if (the_property == m_net_wm_strut) {
00381         updateStrut(winclient);
00382         return true;
00383     }
00384 
00385     return false;
00386 }
00387 
00388 void Ewmh::createAtoms() {
00389 
00390     Display *disp = FbTk::App::instance()->display();
00391 
00392     m_net_supported = XInternAtom(disp, "_NET_SUPPORTED", False);
00393     m_net_client_list = XInternAtom(disp, "_NET_CLIENT_LIST", False);
00394     m_net_client_list_stacking = XInternAtom(disp, "_NET_CLIENT_LIST_STACKING", False);
00395     m_net_number_of_desktops = XInternAtom(disp, "_NET_NUMBER_OF_DESKTOPS", False);
00396     m_net_desktop_geometry = XInternAtom(disp, "_NET_DESKTOP_GEOMETRY", False);
00397     m_net_desktop_viewport = XInternAtom(disp, "_NET_DESKTOP_VIEWPORT", False);
00398     m_net_current_desktop = XInternAtom(disp, "_NET_CURRENT_DESKTOP", False);
00399     m_net_desktop_names = XInternAtom(disp, "_NET_DESKTOP_NAMES", False);
00400     m_net_active_window = XInternAtom(disp, "_NET_ACTIVE_WINDOW", False);
00401     m_net_workarea = XInternAtom(disp, "_NET_WORKAREA", False);
00402     m_net_supporting_wm_check = XInternAtom(disp, "_NET_SUPPORTING_WM_CHECK", False);
00403     m_net_virtual_roots = XInternAtom(disp, "_NET_VIRTUAL_ROOTS", False);
00404 
00405     m_net_close_window = XInternAtom(disp, "_NET_CLOSE_WINDOW", False);
00406     m_net_moveresize_window = XInternAtom(disp, "_NET_MOVERESIZE_WINDOW", False);
00407     
00408     // TODO: implement this one
00409     m_net_wm_moveresize = XInternAtom(disp, "_NET_WM_MOVERESIZE", False);
00410 
00411     m_net_properties = XInternAtom(disp, "_NET_PROPERTIES", False);
00412     m_net_wm_name = XInternAtom(disp, "_NET_WM_NAME", False);
00413     m_net_wm_desktop = XInternAtom(disp, "_NET_WM_DESKTOP", False);
00414     m_net_wm_window_type = XInternAtom(disp, "_NET_WM_WINDOW_TYPE", False);
00415     
00416     // state atom and the supported state atoms
00417     m_net_wm_state = XInternAtom(disp, "_NET_WM_STATE", False);
00418     m_net_wm_state_sticky = XInternAtom(disp, "_NET_WM_STATE_STICKY", False);
00419     m_net_wm_state_shaded = XInternAtom(disp, "_NET_WM_STATE_SHADED", False);
00420     m_net_wm_state_maximized_horz = XInternAtom(disp, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
00421     m_net_wm_state_maximized_vert = XInternAtom(disp, "_NET_WM_STATE_MAXIMIZED_VERT", False);
00422     m_net_wm_state_fullscreen = XInternAtom(disp, "_NET_WM_STATE_FULLSCREEN", False);
00423     
00424     m_net_wm_strut = XInternAtom(disp, "_NET_WM_STRUT", False);
00425     m_net_wm_icon_geometry = XInternAtom(disp, "_NET_WM_ICON_GEOMETRY", False);
00426     m_net_wm_icon = XInternAtom(disp, "_NET_WM_ICON", False);
00427     m_net_wm_pid = XInternAtom(disp, "_NET_WM_PID", False);
00428     m_net_wm_handled_icons = XInternAtom(disp, "_NET_WM_HANDLED_ICONS", False);
00429 
00430     m_net_wm_ping = XInternAtom(disp, "_NET_WM_PING", False);
00431 }
00432 
00433 
00434 void Ewmh::setFullscreen(FluxboxWindow &win, bool value) {
00435     // fullscreen implies maximised, above dock layer, 
00436     // and no decorations (or decorations offscreen)
00437     WindowState *saved_state = getState(win);
00438     if (value) {
00439         // fullscreen on
00440         if (!saved_state) { // not already fullscreen
00441             saved_state = new WindowState(win.x(), win.y(), win.width(),
00442                                           win.height(), win.layerNum(), win.decorationMask());
00443             saveState(win, saved_state);
00444 
00445             // actually make it fullscreen
00446 
00447             // clear decorations
00448             win.setDecorationMask(0);
00449 
00450             // be xinerama aware
00451             BScreen &screen = win.screen();
00452             int head = screen.getHead(win.fbWindow());
00453             win.moveResize(screen.getHeadX(head), screen.getHeadY(head),
00454                            screen.getHeadWidth(head), screen.getHeadHeight(head));
00455             win.moveToLayer(Fluxbox::instance()->getAboveDockLayer());
00456         }
00457     } else { // turn off fullscreen
00458         if (saved_state) { // no saved state, can't restore it
00459             win.setDecorationMask(saved_state->decor);
00460             win.moveResize(saved_state->x, saved_state->y,
00461                            saved_state->width, saved_state->height);
00462             win.moveToLayer(saved_state->layer);
00463             clearState(win);
00464             saved_state = 0;
00465         }
00466     }
00467 }
00468 
00469 // set window state
00470 void Ewmh::setState(FluxboxWindow &win, Atom state, bool value) {
00471 
00472     if (state == m_net_wm_state_sticky) { // STICKY
00473         if (value && !win.isStuck() ||
00474             (!value && win.isStuck()))
00475             win.stick();
00476     } else if (state == m_net_wm_state_shaded) { // SHADED
00477         if ((value && !win.isShaded()) ||
00478             (!value && win.isShaded()))
00479             win.shade();
00480     }  else if (state == m_net_wm_state_maximized_horz ) { // maximized Horizontal
00481         if ((value && !win.isMaximized()) ||
00482             (!value && win.isMaximized()))
00483             win.maximizeHorizontal();
00484     } else if (state == m_net_wm_state_maximized_vert) { // maximized Vertical
00485         if ((value && !win.isMaximized()) ||
00486             (!value && win.isMaximized()))
00487         win.maximizeVertical();
00488     } else if (state == m_net_wm_state_fullscreen) { // fullscreen
00489         setFullscreen(win, value);
00490     }
00491 }
00492 
00493 // toggle window state
00494 void Ewmh::toggleState(FluxboxWindow &win, Atom state) {
00495     if (state == m_net_wm_state_sticky) {
00496         win.stick();
00497     } else if (state == m_net_wm_state_shaded){
00498         win.shade();
00499     } else if (state == m_net_wm_state_maximized_horz ) { // maximized Horizontal
00500         win.maximizeHorizontal();
00501     } else if (state == m_net_wm_state_maximized_vert) { // maximized Vertical
00502         win.maximizeVertical();
00503     } else if (state == m_net_wm_state_fullscreen) { // fullscreen
00504         setFullscreen(win, getState(win) == 0); // toggle current state
00505     }
00506 }
00507 
00508 
00509 void Ewmh::updateStrut(WinClient &winclient) {
00510     Atom ret_type = 0;
00511     int fmt = 0;
00512     unsigned long nitems = 0, bytes_after = 0;
00513     long *data = 0;
00514     if (winclient.property(m_net_wm_strut, 0, 4, False, XA_CARDINAL,
00515                                  &ret_type, &fmt, &nitems, &bytes_after,
00516                                  (unsigned char **) &data) && data) {
00517 #ifdef DEBUG
00518         cerr<<__FILE__<<"("<<__FUNCTION__<<"): Strut: "<<data[0]<<", "<<data[1]<<", "<<
00519             data[2]<<", "<<data[3]<<endl;
00520 #endif // DEBUG
00521             winclient.setStrut(
00522                 winclient.screen().requestStrut(data[0], data[1], data[2], data[3]));
00523             winclient.screen().updateAvailableWorkspaceArea();
00524     }
00525 }
00526 
00527 Ewmh::WindowState::WindowState(int t_x, int t_y, 
00528                                unsigned int t_width, 
00529                                unsigned int t_height,
00530                                int t_layer, unsigned int t_decor) :
00531     x(t_x), y(t_y),
00532     layer(t_layer),
00533     width(t_width),
00534     height(t_height),
00535     decor(t_decor)
00536 {}
00537 
00538 Ewmh::WindowState *Ewmh::getState(FluxboxWindow &win) {
00539     SavedState::iterator it = m_savedstate.find(&win);
00540     if (it == m_savedstate.end())
00541         return 0;
00542     else
00543         return it->second;
00544 }
00545 
00546 void Ewmh::clearState(FluxboxWindow &win) {
00547     WindowState *state = 0;
00548     SavedState::iterator it = m_savedstate.find(&win);
00549     if (it == m_savedstate.end())
00550         return;
00551 
00552     state = it->second;
00553 
00554     m_savedstate.erase(it);
00555     delete state;
00556 }
00557 
00558 void Ewmh::saveState(FluxboxWindow &win, WindowState *state) {
00559     m_savedstate[&win] = state;
00560 }

Fluxbox CVS-Jan-2003




      



Got comments about the page? Send them to webmaster.
If you have general Fluxbox related questions ask them on our irc channel or mailing lists.

Show Source








Designed by aLEczapKA