M Border.C +49 -8
@@ 4,12 4,17 @@
#include "Manager.h"
#include "Rotated.h"
+#if CONFIG_USE_PIXMAPS != False
+#include <X11/xpm.h>
+#include "background.xpm"
+#endif
+
// These distances all exclude the 1-pixel borders. You could
// probably change these a certain amount before breaking the shoddy
// code in the rest of this file.
int Border::m_tabTopHeight = 2;
-int Border::m_frameWidth = 7;
+int Border::m_frameWidth = CONFIG_FRAME_THICKNESS;
int Border::m_transientFrameWidth = 4;
// These are degenerate initialisations, don't change them
@@ 22,6 27,9 @@ unsigned long Border::m_backgroundPixel;
unsigned long Border::m_frameBackgroundPixel;
unsigned long Border::m_buttonBackgroundPixel;
unsigned long Border::m_borderPixel;
+Pixmap Border::m_backgroundPixmap = None;
+
+static int borderCounter = 0;
Border::Border(Client *const c, Window child) :
@@ 87,7 95,23 @@ Border::Border(Client *const c, Window c
if (!m_drawGC) {
windowManager()->fatal("couldn't allocate border GC");
}
+
+#if CONFIG_USE_PIXMAPS != False
+ if (CONFIG_USE_PIXMAPS) {
+ XpmAttributes attrs;
+ attrs.valuemask = 0L;
+ if (XpmCreatePixmapFromData(display(), root(), background,
+ &m_backgroundPixmap, NULL, &attrs)
+ != XpmSuccess) {
+ fprintf(stderr, "wm2: couldn't create background pixmap\n");
+ m_backgroundPixmap = None;
+ }
+ } else
+#endif
+ m_backgroundPixmap = None;
}
+
+ ++borderCounter;
}
@@ 106,6 130,13 @@ Border::~Border()
}
if (m_label) free(m_label);
+
+ if (--borderCounter == 0) {
+ XFreeGC(display(), m_drawGC);
+ if (m_backgroundPixmap != None) {
+ XFreePixmap(display(), m_backgroundPixmap);
+ }
+ }
}
@@ 456,7 487,7 @@ void Border::resizeTab(int h)
operation = ShapeSubtract;
}
- r.x = 0; r.y = shorter - 2;
+ r.x = 0; r.y = shorter /*- 2*/;
r.width = m_tabWidth + 2; r.height = longer - shorter;
XShapeCombineRectangles(display(), m_parent, ShapeBounding,
@@ 473,14 504,16 @@ void Border::resizeTab(int h)
XShapeCombineRectangles(display(), m_tab, ShapeClip,
0, 0, &r, 1, operation, YXBanded);
- // restore a bit of the frame edge
- r.x = m_tabWidth + 1; r.y = shorter;
- r.width = m_frameWidth - 1; r.height = longer - shorter;
- XShapeCombineRectangles(display(), m_parent, ShapeBounding,
- 0, 0, &r, 1, ShapeUnion, YXBanded);
+ if (m_client->isActive()) {
+ // restore a bit of the frame edge
+ r.x = m_tabWidth + 1; r.y = shorter;
+ r.width = m_frameWidth - 1; r.height = longer - shorter;
+ XShapeCombineRectangles(display(), m_parent, ShapeBounding,
+ 0, 0, &r, 1, ShapeUnion, YXBanded);
+ }
for (i = 1; i < m_tabWidth - 1; ++i) {
- r.x = i; r.y = m_tabHeight + i - 3;
+ r.x = i; r.y = m_tabHeight + i - 1/*3*/;
r.width = m_tabWidth - i + 2; r.height = 1;
rl.append(r);
}
@@ 688,6 721,14 @@ void Border::configure(int x, int y, int
ButtonPressMask | ButtonReleaseMask/* | LeaveWindowMask*/);
XSelectInput(display(), m_resize, ButtonPressMask | ButtonReleaseMask);
mask |= CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
+
+ if (m_backgroundPixmap) {
+ XSetWindowAttributes wa;
+ wa.background_pixmap = m_backgroundPixmap;
+ XChangeWindowAttributes(display(), m_parent, CWBackPixmap, &wa);
+ XChangeWindowAttributes(display(), m_tab, CWBackPixmap, &wa);
+ XChangeWindowAttributes(display(), m_button, CWBackPixmap, &wa);
+ }
}
XWindowChanges wc;
M Border.h +1 -0
@@ 88,6 88,7 @@ private:
static unsigned long m_frameBackgroundPixel;
static unsigned long m_buttonBackgroundPixel;
static unsigned long m_borderPixel;
+ static Pixmap m_backgroundPixmap;
};
M Buttons.C +63 -9
@@ 13,6 13,11 @@
void WindowManager::eventButton(XButtonEvent *e)
{
+ if (e->button == Button3) {
+ circulate(e->window == e->root);
+ return;
+ }
+
Client *c = windowToClient(e->window);
if (e->window == e->root) {
@@ 27,15 32,52 @@ void WindowManager::eventButton(XButtonE
}
+void WindowManager::circulate(Boolean activeFirst)
+{
+ Client *c = 0;
+ if (activeFirst) c = m_activeClient;
+
+ if (!c) {
+
+ int i, j;
+
+ if (!m_activeClient) i = -1;
+ else {
+ for (i = 0; i < m_clients.count(); ++i) {
+ if (m_clients.item(i) == m_activeClient) break;
+ }
+
+ if (i >= m_clients.count()-1) i = -1;
+ }
+
+ for (j = i + 1;
+ (!m_clients.item(j)->isNormal() ||
+ m_clients.item(j)->isTransient()); ++j) {
+ if (j >= m_clients.count() - 1) j = -1;
+ if (j == i) return; // no suitable clients
+ }
+
+ c = m_clients.item(j);
+ }
+
+ c->activateAndWarp();
+}
+
+
+void Client::activateAndWarp()
+{
+ mapRaised();
+ ensureVisible();
+ XWarpPointer(display(), None, parent(), 0, 0, 0, 0,
+ m_border->xIndent() / 2, m_border->xIndent() + 8);
+ activate();
+}
+
+
void Client::eventButton(XButtonEvent *e)
{
if (e->type != ButtonPress) return;
- if (e->button == Button3) {
- lower();
- return;
- }
-
mapRaised();
if (e->button == Button1) {
@@ 91,8 133,11 @@ void WindowManager::menu(XButtonEvent *e
int i;
ClientList clients;
+ Boolean allowExit = False;
-#define MENU_LABEL(n) ((n)==0? m_menuCreateLabel : clients.item((n)-1)->label())
+#define MENU_LABEL(n) ((n)==0 ? m_menuCreateLabel : \
+ (allowExit && ((n) > clients.count())) ? "[Exit wm2]" \
+ : clients.item((n)-1)->label())
for (i = 0; i < m_hiddenClients.count(); ++i) {
clients.append(m_hiddenClients.item(i));
@@ 107,6 152,12 @@ void WindowManager::menu(XButtonEvent *e
}
int n = clients.count() + 1;
+ int mx = DisplayWidth (display(), m_screenNumber) - 1;
+ int my = DisplayHeight(display(), m_screenNumber) - 1;
+
+ allowExit = ((e->x > mx-3) && (e->y > my-3));
+ if (allowExit) n += 1;
+
int width, maxWidth = 10;
for (i = 0; i < n; ++i) {
width = XTextWidth(m_menuFont, MENU_LABEL(i), strlen(MENU_LABEL(i)));
@@ 119,8 170,6 @@ void WindowManager::menu(XButtonEvent *e
int totalHeight = entryHeight * n + 13;
int x = e->x - maxWidth/2;
int y = e->y - 2;
- int mx = DisplayWidth (display(), m_screenNumber) - 1;
- int my = DisplayHeight(display(), m_screenNumber) - 1;
Boolean warp = False;
if (x < 0) {
@@ 226,7 275,7 @@ void WindowManager::menu(XButtonEvent *e
for (i = 0; i < n; ++i) {
int dx = XTextWidth(m_menuFont, MENU_LABEL(i),
- strlen(MENU_LABEL(i)));
+ strlen(MENU_LABEL(i)));
int dy = i * entryHeight + m_menuFont->ascent + 10;
if (i >= nh) {
@@ 249,6 298,11 @@ void WindowManager::menu(XButtonEvent *e
}
}
+ if (selecting == n-1 && allowExit) {
+ m_signalled = True;
+ return;
+ }
+
if (selecting >= 0) {
if (selecting == 0) {
spawn();
M Client.C +41 -17
@@ 16,6 16,8 @@ Client::Client(WindowManager *const wm,
m_state(WithdrawnState),
m_managed(False),
m_reparenting(False),
+ m_stubborn(False),
+ m_lastPopTime(0L),
m_colormap(None),
m_colormapWinCount(0),
m_colormapWindows(NULL),
@@ 71,7 73,7 @@ void Client::release()
if (m_revert) {
windowManager()->setActiveClient(m_revert);
m_revert->activate();
- } else windowManager()->clearFocus();
+ } else windowManager()->setActiveClient(0);// windowManager()->clearFocus();
} else {
windowManager()->setActiveClient(0);
}
@@ 195,6 197,9 @@ void Client::manage(Boolean mapped)
gravitate(False);
+ // zeros are iffy, should be calling some Manager method
+ int dw = DisplayWidth(display(), 0), dh = DisplayHeight(display(), 0);
+
if (m_w < m_minWidth) {
m_w = m_minWidth; m_fixedSize = False; reshape = True;
}
@@ 202,14 207,15 @@ void Client::manage(Boolean mapped)
m_h = m_minHeight; m_fixedSize = False; reshape = True;
}
- // zeros are iffy, should be calling some Manager method
+ if (m_w > dw - 8) m_w = dw - 8;
+ if (m_h > dh - 8) m_h = dh - 8;
- if (m_x > DisplayWidth(display(), 0) - m_border->xIndent()) {
- m_x = DisplayWidth(display(), 0) - m_border->xIndent();
+ if (m_x > dw - m_border->xIndent()) {
+ m_x = dw - m_border->xIndent();
}
- if (m_y > DisplayHeight(display(), 0) - m_border->yIndent()) {
- m_y = DisplayHeight(display(), 0) - m_border->yIndent();
+ if (m_y > dh - m_border->yIndent()) {
+ m_y = dh - m_border->yIndent();
}
if (m_x < m_border->xIndent()) m_x = m_border->xIndent();
@@ 226,28 232,48 @@ void Client::manage(Boolean mapped)
// (support for shaped windows absent)
XAddToSaveSet(d, m_window);
+ m_managed = True;
if (shouldHide) hide();
else {
XMapWindow(d, m_window);
m_border->map();
+ setState(NormalState);
if (CONFIG_CLICK_TO_FOCUS ||
(m_transient != None && activeClient() &&
activeClient()->m_window == m_transient)) {
activate();
+ mapRaised();
} else {
deactivate();
}
-
- setState(NormalState);
}
if (activeClient() && !isActive()) {
activeClient()->installColormap();
}
- m_managed = True;
+ if (CONFIG_AUTO_RAISE) {
+ m_windowManager->stopConsideringFocus();
+ focusIfAppropriate(False);
+ }
+}
+
+
+void Client::selectOnMotion(Window w, Boolean select)
+{
+ if (!CONFIG_AUTO_RAISE) return;
+ if (!w || w == root()) return;
+
+ if (w == m_window || m_border->hasWindow(w)) {
+ XSelectInput(display(), m_window, // not "w"
+ ColormapChangeMask | EnterWindowMask |
+ PropertyChangeMask | FocusChangeMask |
+ (select ? PointerMotionMask : 0L));
+ } else {
+ XSelectInput(display(), w, select ? PointerMotionMask : 0L);
+ }
}
@@ 271,7 297,7 @@ void Client::activate()
if (isActive()) {
decorate(True);
- if (CONFIG_RAISE_ON_FOCUS) mapRaised();
+ if (CONFIG_AUTO_RAISE || CONFIG_RAISE_ON_FOCUS) mapRaised();
return;
}
@@ 298,7 324,7 @@ void Client::activate()
while (m_revert && !m_revert->isNormal()) m_revert = m_revert->revertTo();
windowManager()->setActiveClient(this);
- if (CONFIG_RAISE_ON_FOCUS) mapRaised();
+// if (CONFIG_AUTO_RAISE || CONFIG_RAISE_ON_FOCUS) mapRaised();
decorate(True);
installColormap(); // new!
@@ 608,7 634,8 @@ void Client::hide()
m_border->unmap();
XUnmapWindow(display(), m_window);
- if (activeClient() == this) windowManager()->clearFocus();
+// if (isActive()) windowManager()->setActiveClient(0);
+ if (isActive()) windowManager()->clearFocus();
setState(IconicState);
windowManager()->addToHiddenList(this);
@@ 629,7 656,8 @@ void Client::unhide(Boolean map)
XMapWindow(display(), m_window);
mapRaised();
- if (CONFIG_CLICK_TO_FOCUS) activate();
+ if (CONFIG_AUTO_RAISE) focusIfAppropriate(False);
+ else if (CONFIG_CLICK_TO_FOCUS) activate();
}
}
@@ 661,10 689,6 @@ void Client::withdraw()
gravitate(True);
XReparentWindow(display(), m_window, root(), m_x, m_y);
-// delete m_border;
-// m_border = new Border(this, m_window);
-// m_parent = root();
-
gravitate(False);
XRemoveFromSaveSet(display(), m_window);
M Client.h +6 -0
@@ 54,6 54,10 @@ public:
void sendMessage(Atom, long);
void sendConfigureNotify();
+ void activateAndWarp();
+ void focusIfAppropriate(Boolean);
+ void selectOnMotion(Window, Boolean);
+
/* for call from within: */
void fatal(char *m) { m_windowManager->fatal(m); }
@@ 105,6 109,8 @@ private:
int m_protocol;
Boolean m_managed;
Boolean m_reparenting;
+ Boolean m_stubborn; // keeps popping itself to the front
+ Time m_lastPopTime;
char *m_name;
char *m_iconName;
M Config.h +20 -7
@@ 8,30 8,43 @@
#define CONFIG_EXEC_USING_SHELL False
#define CONFIG_NEW_WINDOW_COMMAND "xterm"
+#define CONFIG_EVERYTHING_ON_ROOT_MENU False
-// You can't have CLICK_TO_FOCUS True with RAISE_ON_FOCUS False, but
-// the other three combinations seem to work
-
+// You can't have CLICK_TO_FOCUS without RAISE_ON_FOCUS but the other
+// combinations should be okay. If you set AUTO_RAISE you must leave
+// the other two False; you'll then get focus-follows, auto-raise, and
+// a delay on auto-raise as configured in the DELAY settings below.
#define CONFIG_CLICK_TO_FOCUS False
#define CONFIG_RAISE_ON_FOCUS False
+#define CONFIG_AUTO_RAISE False
+
+// milliseconds. In theory these only apply when using AUTO_RAISE,
+// not when just using RAISE_ON_FOCUS without CLICK_TO_FOCUS. First
+// of these is the usual delay before raising; second is the delay
+// after the pointer has stopped moving (only applicable over simple X
+// windows such as xvt).
+#define CONFIG_AUTO_RAISE_DELAY 400
+#define CONFIG_POINTER_STOPPED_DELAY 80
#define CONFIG_TAB_FOREGROUND "black"
#define CONFIG_TAB_BACKGROUND "gray80"
-#define CONFIG_FRAME_BACKGROUND "white"
-#define CONFIG_BUTTON_BACKGROUND "white"
+#define CONFIG_FRAME_BACKGROUND "gray95"
+#define CONFIG_BUTTON_BACKGROUND "gray95"
#define CONFIG_BORDERS "black"
#define CONFIG_MENU_FOREGROUND "black"
#define CONFIG_MENU_BACKGROUND "gray80"
#define CONFIG_MENU_BORDERS "black"
-#define CONFIG_EVERYTHING_ON_ROOT_MENU True
+#define CONFIG_USE_PIXMAPS False
+
+// I like 7 for plain frames, or 8 if using pixmaps
+#define CONFIG_FRAME_THICKNESS 7
// If CONFIG_PROD_SHAPE is True, all frame element shapes will be
// recalculated afresh every time their focus changes. This will
// probably slow things down hideously, but has been reported as
// necessary on some systems (possibly SunOS 4.x with OpenWindows).
-
#define CONFIG_PROD_SHAPE False
#endif
M Events.C +158 -9
@@ 3,7 3,6 @@
#include "Client.h"
-
int WindowManager::loop()
{
XEvent ev;
@@ 85,6 84,12 @@ int WindowManager::loop()
break;
case MotionNotify:
+ if (CONFIG_AUTO_RAISE && m_focusChanging) {
+ if (!m_focusPointerMoved) m_focusPointerMoved = True;
+ else m_focusPointerNowStill = False;
+ }
+ break;
+
case FocusOut:
case ConfigureNotify:
case MapNotify:
@@ 112,9 117,12 @@ void WindowManager::nextEvent(XEvent *e)
int fd;
fd_set rfds;
struct timeval t;
+ int r;
if (!m_signalled) {
+ waiting:
+
if (QLength(m_display) > 0) {
XNextEvent(m_display, e);
return;
@@ 125,6 133,10 @@ void WindowManager::nextEvent(XEvent *e)
FD_SET(fd, &rfds);
t.tv_sec = t.tv_usec = 0;
+#ifdef hpux
+#define select(a,b,c,d,e) select((a),(int *)(b),(c),(d),(e))
+#endif
+
if (select(fd + 1, &rfds, NULL, NULL, &t) == 1) {
XNextEvent(m_display, e);
return;
@@ 132,12 144,20 @@ void WindowManager::nextEvent(XEvent *e)
XFlush(m_display);
FD_SET(fd, &rfds);
+ t.tv_sec = 0; t.tv_usec = 20000;
- if (select(fd + 1, &rfds, NULL, NULL, NULL) == 1) {
+ if ((r = select(fd + 1, &rfds, NULL, NULL,
+ m_focusChanging ? &t : (struct timeval *)NULL)) == 1) {
XNextEvent(m_display, e);
return;
}
+ if (CONFIG_AUTO_RAISE && m_focusChanging) { // timeout on select
+ checkDelaysForFocus();
+ }
+
+ if (r == 0) goto waiting;
+
if (errno != EINTR || !m_signalled) {
perror("wm2: select failed");
m_looping = False;
@@ 150,6 170,93 @@ void WindowManager::nextEvent(XEvent *e)
}
+void WindowManager::checkDelaysForFocus()
+{
+ if (!CONFIG_AUTO_RAISE) return;
+
+ int t = timestamp(True);
+
+ if (m_focusPointerMoved) { // only raise when pointer stops
+
+ if (t < m_focusTimestamp ||
+ t - m_focusTimestamp > CONFIG_POINTER_STOPPED_DELAY) {
+
+ if (m_focusPointerNowStill) {
+ m_focusCandidate->focusIfAppropriate(True);
+// if (m_focusCandidate->isNormal()) m_focusCandidate->mapRaised();
+// stopConsideringFocus();
+
+ } else m_focusPointerNowStill = True; // until proven false
+ }
+ } else {
+
+ if (t < m_focusTimestamp ||
+ t - m_focusTimestamp > CONFIG_AUTO_RAISE_DELAY) {
+
+ m_focusCandidate->focusIfAppropriate(True);
+
+// if (m_focusCandidate->isNormal()) m_focusCandidate->mapRaised();
+// stopConsideringFocus();
+ }
+ }
+}
+
+
+void WindowManager::considerFocusChange(Client *c, Window w, Time timestamp)
+{
+ if (!CONFIG_AUTO_RAISE) return;
+
+ if (m_focusChanging) {
+ stopConsideringFocus();
+ }
+
+ m_focusChanging = True;
+ m_focusTimestamp = timestamp;
+ m_focusCandidate = c;
+ m_focusCandidateWindow = w;
+
+ // we need to wait until at least one pointer-motion event has
+ // come in before we can start to wonder if the pointer's
+ // stopped moving -- otherwise we'll be caught out by all the
+ // windows for which we don't get motion events at all
+
+ m_focusPointerMoved = False;
+ m_focusPointerNowStill = False;
+ m_focusCandidate->selectOnMotion(m_focusCandidateWindow, True);
+}
+
+
+void WindowManager::stopConsideringFocus()
+{
+ if (!CONFIG_AUTO_RAISE) return;
+
+ m_focusChanging = False;
+ if (m_focusChanging && m_focusCandidateWindow) {
+ m_focusCandidate->selectOnMotion(m_focusCandidateWindow, False);
+ }
+}
+
+
+void Client::focusIfAppropriate(Boolean ifActive)
+{
+ if (!CONFIG_AUTO_RAISE) return;
+ if (!m_managed || !isNormal()) return;
+ if (!ifActive && isActive()) return;
+
+ Window rw, cw;
+ int rx, ry, cx, cy;
+ unsigned int k;
+
+ XQueryPointer(display(), root(), &rw, &cw, &rx, &ry, &cx, &cy, &k);
+
+ if (hasWindow(cw)) {
+ activate();
+ mapRaised();
+ m_windowManager->stopConsideringFocus();
+ }
+}
+
+
void WindowManager::eventConfigureRequest(XConfigureRequestEvent *e)
{
XWindowChanges wc;
@@ 221,8 328,33 @@ void Client::eventConfigureRequest(XConf
// if parent==root, it's not managed yet -- & it'll be raised when it is
if (raise && parent() != root()) {
- mapRaised();
- if (CONFIG_CLICK_TO_FOCUS) activate();
+
+ if (CONFIG_AUTO_RAISE) {
+
+ m_windowManager->stopConsideringFocus();
+
+ if (!m_stubborn) { // outstubborn stubborn windows
+ Time popTime = windowManager()->timestamp(True);
+
+ if (m_lastPopTime > 0L &&
+ popTime > m_lastPopTime &&
+ popTime - m_lastPopTime < 2000) { // 2 pops in 2 seconds
+ m_stubborn = True;
+ m_lastPopTime = 0L;
+
+ fprintf(stderr, "wm2: client \"%s\" declared stubborn\n",
+ label());
+
+ } else {
+ m_lastPopTime = popTime;
+ }
+
+ mapRaised();
+ }
+ } else {
+ mapRaised();
+ if (CONFIG_CLICK_TO_FOCUS) activate();
+ }
}
}
@@ 252,18 384,22 @@ void Client::eventMapRequest(XMapRequest
m_border->reparent();
+ if (CONFIG_AUTO_RAISE) m_windowManager->stopConsideringFocus();
XAddToSaveSet(display(), m_window);
- // fall through
+ XMapWindow(display(), m_window);
+ mapRaised();
+ setState(NormalState);
+ if (CONFIG_CLICK_TO_FOCUS) activate();
+ break;
case NormalState:
XMapWindow(display(), m_window);
mapRaised();
- setState(NormalState);
-
if (CONFIG_CLICK_TO_FOCUS) activate();
break;
case IconicState:
+ if (CONFIG_AUTO_RAISE) m_windowManager->stopConsideringFocus();
unhide(True);
break;
}
@@ 295,6 431,7 @@ void Client::eventUnmap(XUnmapEvent *e)
}
m_reparenting = False;
+ m_stubborn = False;
}
@@ 311,6 448,10 @@ void WindowManager::eventDestroy(XDestro
if (c) {
+ if (CONFIG_AUTO_RAISE && m_focusChanging && c == m_focusCandidate) {
+ m_focusChanging = False;
+ }
+
for (int i = m_clients.count()-1; i >= 0; --i) {
if (m_clients.item(i) == c) {
m_clients.remove(i);
@@ 456,8 597,15 @@ void Client::eventEnter(XCrossingEvent *
if (activeClient()->coordsInHole(e->x - x, e->y - y)) return;
}
- if (e->type == EnterNotify && !isActive() && !CONFIG_CLICK_TO_FOCUS) {
- activate();
+ if (e->type == EnterNotify) {
+ if (!isActive() && !CONFIG_CLICK_TO_FOCUS) {
+ activate();
+ if (CONFIG_AUTO_RAISE) {
+ windowManager()->considerFocusChange(this, m_window, e->time);
+ } else if (CONFIG_RAISE_ON_FOCUS) {
+ mapRaised();
+ }
+ }
}
}
@@ 488,6 636,7 @@ void Client::eventFocusIn(XFocusInEvent
{
if (m_window == e->window && !isActive()) {
activate();
+ mapRaised();
}
}
M General.h +4 -0
@@ 6,6 6,10 @@
#include <sys/types.h>
#include <sys/time.h>
+#ifdef _POSIX_SOURCE
+#undef _POSIX_SOURCE
+#endif
+
#define _POSIX_SOURCE 1
#include <stdio.h>
M Makefile +9 -3
@@ 1,5 1,7 @@
-LIBS = -lXext -lX11 -lXmu -lm
+# if you want pixmaps you'll need Xpm, so add -lXpm to the next line
+LIBS = -L/usr/X11/lib -lXext -lX11 -lXmu -lm
+
CC = gcc
CCC = gcc
CFLAGS = -O2
@@ 12,11 14,15 @@ OBJECTS = Border.o Buttons.o Client.o Ev
$(CCC) -c $(CFLAGS) $<
wm2: $(OBJECTS)
- mv -f wm2 wm2.old ; $(CCC) -o wm2 $(OBJECTS) $(LIBS)
+ mv -f wm2 wm2.old >& /dev/null || true
+ $(CCC) -o wm2 $(OBJECTS) $(LIBS)
depend:
makedepend -- $(CFLAGS) -- *.C
clean:
- rm -f *.o wm2 core
+ rm -f *.o core
+distclean: clean
+ rm -f wm2 wm2.old *~
+
M Manager.C +43 -19
@@ 25,37 25,59 @@ implementPList(ClientList, Client);
WindowManager::WindowManager() :
- m_menuGC(0), m_menuWindow(0), m_menuFont(0)
+ m_menuGC(0), m_menuWindow(0), m_menuFont(0), m_focusChanging(False)
{
- fprintf(stderr, "\nwm2: Copyright (c) 1996 Chris Cannam\n"
+ fprintf(stderr, "\nwm2: Copyright (c) 1996-7 Chris Cannam."
+ " Third release, January 1997\n"
" Parts derived from 9wm Copyright (c) 1994-96 David Hogan\n"
" %s\n Copying and redistribution encouraged. "
- "No warranty.\n", XV_COPYRIGHT);
+ "No warranty.\n\n", XV_COPYRIGHT);
- if (CONFIG_CLICK_TO_FOCUS) {
- if (CONFIG_RAISE_ON_FOCUS) {
- fprintf(stderr, " Click to focus. ");
+ if (CONFIG_AUTO_RAISE) {
+ if (CONFIG_CLICK_TO_FOCUS) {
+ fatal("can't have auto-raise-with-delay with click-to-focus");
+ } else if (CONFIG_RAISE_ON_FOCUS) {
+ fatal("can't have raise-on-focus AND auto-raise-with-delay");
} else {
- fatal("can't have click to focus without auto-raise");
+ fprintf(stderr, " Focus follows, auto-raise with delay. ");
}
+
} else {
- if (CONFIG_RAISE_ON_FOCUS) {
- fprintf(stderr, " Focus follows, auto-raise. ");
+ if (CONFIG_CLICK_TO_FOCUS) {
+ if (CONFIG_RAISE_ON_FOCUS) {
+ fprintf(stderr, " Click to focus. ");
+ } else {
+ fatal("can't have click-to-focus without raise-on-focus");
+ }
} else {
- fprintf(stderr, " Focus follows pointer. ");
+ if (CONFIG_RAISE_ON_FOCUS) {
+ fprintf(stderr, " Focus follows, auto-raise. ");
+ } else {
+ fprintf(stderr, " Focus follows pointer. ");
+ }
}
}
- if (CONFIG_PROD_SHAPE) {
- fprintf(stderr, "Shape prodding. ");
+ if (CONFIG_EVERYTHING_ON_ROOT_MENU) {
+ fprintf(stderr, "All clients on menu.\n");
+ } else {
+ fprintf(stderr, "Hidden clients only on menu.\n");
}
- if (CONFIG_EVERYTHING_ON_ROOT_MENU) {
- fprintf(stderr, "All clients on menu.\n\n");
+ if (CONFIG_PROD_SHAPE) {
+ fprintf(stderr, " Shape prodding on. ");
} else {
- fprintf(stderr, "Hidden clients only on menu.\n\n");
+ fprintf(stderr, " Shape prodding off. ");
}
+ if (CONFIG_USE_PIXMAPS) {
+ fprintf(stderr, "Fancy borders.");
+ } else {
+ fprintf(stderr, "Plain borders.");
+ }
+
+ fprintf(stderr, "\n (To reconfigure, simply edit and recompile.)\n\n");
+
m_display = XOpenDisplay(NULL);
if (!m_display) fatal("can't open display");
@@ 94,10 116,9 @@ WindowManager::WindowManager() :
XSync(m_display, False);
m_initialising = False;
m_returnCode = 0;
-
+
clearFocus();
scanInitialWindows();
-
loop();
}
@@ 329,7 350,7 @@ void WindowManager::installCursorOnWindo
}
-int WindowManager::timestamp(Boolean reset)
+Time WindowManager::timestamp(Boolean reset)
{
if (reset) m_currentTime = CurrentTime;
@@ 411,7 432,10 @@ void WindowManager::clearFocus()
static Window w = 0;
Client *active = activeClient();
- if (!CONFIG_CLICK_TO_FOCUS) return;
+ if (CONFIG_AUTO_RAISE || !CONFIG_CLICK_TO_FOCUS) {
+ setActiveClient(0);
+ return;
+ }
if (active) {
M Manager.h +13 -1
@@ 21,7 21,7 @@ public:
Client *windowToClient(Window, Boolean create = False);
Client *activeClient() { return m_activeClient; }
Boolean raiseTransients(Client *); // true if raised any
- int timestamp(Boolean reset);
+ Time timestamp(Boolean reset);
void clearFocus();
void setActiveClient(Client *const c) { m_activeClient = c; }
@@ 41,6 41,9 @@ public:
void installCursorOnWindow(RootCursor, Window);
void installColormap(Colormap);
+ void considerFocusChange(Client *, Window, Time timestamp);
+ void stopConsideringFocus();
+
// shouldn't really be public
int attemptGrab(Window, Window, int, int);
void releaseGrab(XButtonEvent *);
@@ 97,6 100,15 @@ private:
const char *const menuLabel(int);
void menu(XButtonEvent *);
void spawn();
+ void circulate(Boolean activeFirst);
+
+ Boolean m_focusChanging; // checking times for focus change
+ Client *m_focusCandidate;
+ Window m_focusCandidateWindow;
+ Time m_focusTimestamp; // time of last crossing event
+ Boolean m_focusPointerMoved;
+ Boolean m_focusPointerNowStill;
+ void checkDelaysForFocus();
void nextEvent(XEvent *); // return
M README +60 -27
@@ 2,11 2,11 @@
wm2 -- a window manager
=======================
-wm2 is a new window manager for X. It provides an unusual style of
-window decoration and as little functionality as I feel comfortable
-with in a window manager. wm2 is not configurable, except by editing
-the source and recompiling the code, and is really intended for people
-who don't particularly want their window manager to be too friendly.
+wm2 is a window manager for X. It provides an unusual style of window
+decoration and as little functionality as I feel comfortable with in a
+window manager. wm2 is not configurable, except by editing the source
+and recompiling the code, and is really intended for people who don't
+particularly want their window manager to be too friendly.
wm2 provides:
@@ 24,8 24,8 @@ wm2 provides:
This release
============
-This is the second release of wm2. It differs in some ways from the
-first release.
+This is the third release of wm2, made in January 1997. It differs in
+some ways from the first and second releases.
Building wm2
@@ 34,8 34,10 @@ Building wm2
You will need a Unix machine, X libraries and a C++ compiler such as
gcc. You will also need a mouse, with at least one button. Your X
server and libraries must be R4 or newer and must support the Shape
-extension. wm2 does NOT support multi-screen displays, because I
-don't have anything to test multi-screen code on.
+extension. If you want to use frame background-pixmaps (see "Pixmaps"
+below), you will need the Xpm header and library. wm2 does NOT
+support multi-screen displays, because I don't have anything to test
+multi-screen code on.
wm2 makes relatively heavy demands on the performance of your X
server, because of the use of shaped windows, but it shouldn't place
@@ 67,11 69,7 @@ definitions in Config.h and recompiling)
Available window manipulations are:
-- To focus a window: depends on the focus policy you selected
- in Config.h before compiling. The default distribution has
- focus-follows-pointer; if you prefer click-to-focus, edit
- and rebuild. You can also choose focus-follows with auto-
- raise, which should work a bit better in the second release
- than it did in the first.
+ in Config.h before compiling. See "Focus policy", below.
-- To raise a window: click on its tab or frame, unless you have
auto-raise on focus set in Config.h.
@@ 100,12 98,52 @@ Available window manipulations are:
the enclosing window frame.
-- To lower a window: click with the right mouse button on its
- tab or frame. (This is the only new feature in the second
+ tab or frame. (This was the only new feature in the second
release.)
+ -- To exit from wm2: move the mouse pointer to the very edge of the
+ screen at the extreme lower-right corner, and click left button on
+ the root window for the root menu. The menu should have an extra
+ option labelled "Exit wm2"; select this. (This is a new feature
+ in the third release.)
+
All move and resize operations are opaque.
+Focus policy
+============
+
+Config.h contains settings for focus policy. There are three things
+you can define to either True or False: CONFIG_CLICK_TO_FOCUS,
+CONFIG_RAISE_ON_FOCUS and CONFIG_AUTO_RAISE. The first two are
+connected: together they define a focus policy. The third is a
+separate focus policy on its own and will only work if the first two
+are both False. CONFIG_AUTO_RAISE differs from
+(!CONFIG_CLICK_TO_FOCUS && CONFIG_RAISE_ON_FOCUS) only in that it
+provides a short delay before raising each window. The delay is also
+definable in Config.h.
+
+
+Pixmaps
+=======
+
+This third release of wm2 will allow you to specify a background
+pixmap for the window frames. If you set CONFIG_USE_PIXMAPS to True
+in Config.h and supply a file ./background.xpm which contains an Xpm
+pixmap called "background", wm2 will use this to colour in the frames.
+An especially unpleasant example file, cribbed and modified from
+http://home.netscape.com/assist/net_sites/bg/marble/greenred_marble.gif,
+is included, although you're advised to find your own.
+
+
+xterm
+=====
+
+Some versions of xterm run badly under wm2. If you use xterm and find
+that it refreshes the window excessively slowly, you might like to try
+using xvt or rxvt instead.
+
+
Credits
=======
@@ 122,23 160,18 @@ font-rotation routines.
The sideways tabs on the window frames were Andy Green's idea.
-
-Bugs &c
-=======
-
-If you find a bug, please report it to me (preferably with a fix). If
-you want a new feature added, you can try asking but I'm unlikely to
-do it (unless it's something really original and very easy to do --
-which seems improbable). If you've already written the code to add
-the feature, I'd probably quite like to see it but I'm unlikely to
-include it in the main distribution unless I really like it (or it's
-something "essential", like multi-headed display support).
-
If you want to hack the code into something else for your own
amusement, please go ahead. Feel free to modify and redistribute, as
long as you retain the original copyrights as appropriate.
+Bugs
+====
+
+The principal bug is that wm2 now has too many features. That aside,
+if you find a bug, please report it to me (preferably with a fix).
+
+
Chris Cannam, cannam@zands.demon.co.uk
October 1996
A => background.xpm +114 -0
@@ 0,0 1,114 @@
+/* XPM */
+static char *background[] = {
+/* width height num_colors chars_per_pixel */
+" 80 80 27 1",
+/* colors */
+". c #b5ae84",
+"# c #ada67b",
+"a c #ada884",
+"b c #b5ae7b",
+"c c #b5b084",
+"d c #ada97b",
+"e c #adaa84",
+"f c #b5b28c",
+"g c #b5b384",
+"h c #b5b58c",
+"i c #b4b584",
+"j c #b3b594",
+"k c #b2b58c",
+"l c #b7bd8c",
+"m c #b8bd94",
+"n c #c0c694",
+"o c #c5ce94",
+"p c #bec694",
+"q c #cad694",
+"r c #c2ce94",
+"s c #d8e79c",
+"t c #cfde94",
+"u c #ddef9c",
+"v c #d0de9c",
+"w c #d5e794",
+"x c #e2f79c",
+"y c #d5e79c",
+/* pixels */
+"gggggiiiihhiffiffghfgfhhhhhhhhhkkkllmmppnpppprrrrrppppppnppnmmmllkkkkkkhhhhgffff",
+"fhffiiieihhhhhfihhhhhehhfhhhhhhhkkkkklmlmlnppnrrorrnppnpppppnnnmmllkjjkkkhhhhhhh",
+"hhhhhhhhhhhhhhhhhihhhihhhfhhihhhkhkkkkmlmlplpnnorrnpporrorppnnpnlplmmljjkkkkhhhh",
+"kkkkkkkkkkkhhkkkkhhhhhhihhfhhhhhhkhhkkkkjmmlplpnnppnoroooooporrpppppplllljjkkkkk",
+"kkkkkkkkkkkkkkkkhhkhhhhfeiffffhhihhhhkkkjkkmmmppppnnnooorrorrroonpnnpmmmljmjkkkk",
+"klkkkkkkkkkkkkkkhhhhhihhiieiifhhheeiihhhhkkkklmmlpnnonoooororrorrrrronnplmmmmjmk",
+"mjlmllkjkjkkkkjjjhhkhhhhhihfeeeefgeeeffihhhhhkjmllpponpooooroqqoooooorppnpnplmml",
+"mmlmlllmmllklkkkkhhhhhhieihigeieggeeegiegehhhhhkkllllplnoooooqqqorqooooronnnnnpl",
+"plpppplmlmlllkkkjjkkhhhieehgggeeceeeeceggeghhihhhkjkllnrpooroqqqqrooqrrrpponplpp",
+"plpnlpplmllmlkjjkjkhhhieeefegecccbbbbdddeeegehefhhhkklllpnnrorqrqrqqqqqooonplrop",
+"pponlnppmlllllllkkkkkhhihfieggcdebbbbbd..ddbeegfehhhkkmmmnnnnrorqrqqrqrooorrooro",
+"ppnnpplplpmmlmllmmkkkkhhfiiegceedddab.dddadbddeeegeihhkkmmponoorqorroqqoqoorrrrp",
+"oonooonpnpppllmmlkkkkkkhhheggcceeddd#a######.bdeeeegehhjmmmlnnporqqoqqorqqrrrrrr",
+"rrrrrrprppnnnmpmlklkkkkhfhiieeeee..add########ddddegeihhkjmlppnrrorqoqooqqrrrorr",
+"ooooooooooppnnpnmmmmjjkkhhiegeeeeaaad###########abbcgefhhklmplprroqqqqqqqqrorqro",
+"oqqooooooonronnnmplmmlkkkhhfhfecc..d.d##########b.a.cehfhhkjmmnpproooqqqqqqqqqqq",
+"qrrrrooooooonnnnppmmlmlkjkhhhhfgcccc.bd##########aadecgehhkkklmpnpooooooqqoooorr",
+"rqooqorqqqorrorpppnnnmmllkkhhifffgcbc.bdb##########d.ccgfhhkjjlpppooroooqqqqqqqo",
+"qoovovvrqvoqroorrrpppmnlmmjkkhhhiegeeeda#d########adaeeegihhkmlmpprroorqoqqooooo",
+"qrrqvvvqovovqqqoorronppmpmljkkkhhheggcedddab#######addeegeihhjjmpnnooorqqorooooq",
+"qqqvqqoqqqqqoovvqoroponnnpmmjjjkhhhhfeceedddd###bb#db.eccehhhkjmmmnnroooororooqo",
+"qqqqqvvqqvooqvvvvvqqorrrppnnlmmjjhhhigggceead#b#b#babddccgiehhkkmnpppproorqqqqqq",
+"qqqvqvvvqqqvvqqqqqovoqrooopppmmmjkkhhhffccedddbdb#.abb.ccgeihkjjmmlpprrororrqroq",
+"qqoqqqqqqvqqqqvqqqqqvvvqoorrppppmlkkkhhiiggcbeeddaddd.cccgghihkjjlmmnnprrrqorqqq",
+"qqqvqvvtqqtqttvqqqvqqqqoqqorooppmmlkkhhhhgggcccbadddddeeeeeeihkkkmllnnnooooqoroo",
+"qqqqvvvqtvqqttvtqvttqvvqqqrqorooppplmkkkhhhgeeecdeeeaacccgffehhkkkmplppoooorqorq",
+"qoqvqqqqqttqttvtvvttttvqqqvvqqooppnmmlkkkkhfgheeeeeeeeeecgffehhkkkmmpppprorooror",
+"ooqqqqqqttvvttttvvvvtvvvttvvqqqooropmpmmkkkhhhffggcceeeeeeeifihhkklmpnpnnoorqrqo",
+"qqovqqvqttvqtttttttttvvvvvvvqvqoooorrnpmmjjkhhfhffgeeeeeeeegehhhkklmmppproroorqo",
+"rqqqqqqttqttvvttttttttttttttttvqqororopmplmjkhhhfheffgggeffhhhhhhkjmmpnprorroqoq",
+"orovqqvqqtqvtvvssyvsvstttwttttttqqvroopnnpmlkkkkhhhihgfgfiiefhhhhkjmmpnpprroroqr",
+"roqqqqqqqqqvtvtttttvyywwwwstvvvvvvqqqqoropmpmmjjkkhheeeehhhfhihkhkjmmlppprpooooo",
+"qqqqqqqqtvvtvtttttwvvswwwvtyyvvtttttqqvqooonpmmjjkhhhhhihhhhihhhhkmmmnnppppooooq",
+"rorooqqqqtvtvttttttvvvwwvwywssywwttvtqvvvroopnlmmmkkkhhhhhihhhkkkkjjmmlppprrooro",
+"qqqqqqqqqtqqvtvvvsvvtstvwswyywywwwvtvvtvvvqronnmmmjjjkkkhhhhkhhkhklmmlmpnppprooq",
+"rorrqqqqqqtqttttttttvtvtytswyyywwywstwtvtvqooonnnnmllmjjkhkkkkkkjmmmmlnnnppnoooo",
+"qqqoroqoqqqqvqvvvvsswvwvwywssssssyswwwtsvvtqqqropppnmmlllkkkkkkjjjmmmlmnnpprrroq",
+"orqqqrqqqqqqtttvvvvvttvtswssywwwusywswwwsvtvqqoqorrnnplmmmmmlllmmmmmmnlnpprrroro",
+"oorqqorqqqqqqqvtttttttwtwvyyswysssuyyssssyvsvqqovoooopppnmmlmmmmmmmpnlpnooorrooo",
+"orqqrqvoooqqqqtvvvtvvvvvstsvywwwyswywywuyysysvvtvooqooonnnmplmlmmlmlplnnrrooooro",
+"qqqqorrooqqqqqqqvvtvvvsvvsytvwsssyuuuuuuysyysvstvqqqqororppnpnpnnnnnpnnoooooooqq",
+"orrqrooqooqqorqqvqqtvvtttttwtvysywywwwwyyyuwsssysvvtvqvvooooonppppnpprppprrrooro",
+"qqqrorooooqrrqqqqqvqqvqqtqttttvvyywywywwwuyyuwssssysvtvvvqqooooorrprorrrrroorrqq",
+"orqoroqooqqqoqooooqvqqvqqqqvvvsvvvywssuuyyuyuuyywysyywvvtvqqqoqooorroorroooroooo",
+"qqrqqqqrroooqoqorqrrovqqvqqqvttttvvtwssusuywuyyywussssvsvvtqqvvqrqqqqrrqoqqqqooq",
+"qqoorrrooooooorooooqrrrrvovqqqqqvvvvvtwwysuywyxuyyuywwsyvvtvvvtqqqrqrvqorqoqqovq",
+"qrroooooroorrrrorooorrooooooqqovqqvvvtvswwwyuuuuxuxyyyywsywwttttttqqqtqovvqvqqqq",
+"rqvvvqroorroooorrppnprppnororroqvoqqvttvwwsssuuuyyuyuywyuuusyssvwvtttqqqqqqrqqvq",
+"qqqorrooroorrroornnpnnnnppnnprroorqvvqqtvvsywysuuxuuyyyuxxuuuswswvwvvvvvqtqvqqqq",
+"qqqvoqroorrooppopnnpppppnpppppnpnpoorovqvqttwwsyuuuxuyxxyxxxuuywwsyvwtwttttvvqqq",
+"qqooororroorppnppnnmnlmlmnmmnnnppppprorqooqqqtsyssuuyuxxxxxxyxxuuusyytsvvttvvqqq",
+"qqqrorqrrrrooonnppnlmlmlmlmlmlmmmmmmnnpporovqqvsvywwwyuxyxxxuyxxxuuuswysvvtvttvt",
+"qqroqrorooorpnnnnnppmmmlmlkkkkkkkjlmmnpnnporoqqvvvswwuwuuyyuyxxwuwyyuuswwwttttvq",
+"qqqqqrqorrrororrpnmllmlmkkkkkjkjkkjjmklmnppproqvvqvvvsuyuyuxxxuxxuyuuuswywttttqq",
+"tqqqqrooororrooonppmlmlklkkkhhhhhkkkkkjmlmmpnnoroqvvvvssywwuxyyxwyuxuuuwwyvyvttv",
+"tqqovroroooorpoprppmlmlljjkkjkkhhhhkhhkkkkknmppporovvtttywwyuuyywuwyywuwswyyvttv",
+"qqqqqoqorroooororrnnnmmmmkkkkkkhhhhhhkhhkkkmmnmnnrorvqvtttywyywyyywuuuuywysttttv",
+"vqqqqqqooqoorqqorrrnpppnmllmkjkhkhkhhhhhkkkkkjlmnpnpooqqqtttywwyyuuuuussywvvvvvv",
+"vqqqqrqqqqrqqqvqqoooonnppnpmmmjkkkkkhhhhkhhhjklmmnpporoovqqvvttssywyssyswwttvvvq",
+"qvqqqoqvvrqqqvvqqqvvqrorrnnpllmlmkkjjhhkhhhhkkkmmmnmnnnorvqvqttvvtvwtwtywtttvvtq",
+"vqqqqqqqqqqqqqqqqqqqqorooorppnpmmmlkkkkkjjjkkkjjjlmmnpprroqqqqtvvttttvvtttttvtqv",
+"qqqqqqvvvqqqtqvvttvqtqqqqrqoronnnplmmmmlkkjjjkjjjmlmlppppprqoqqqqqtttvvtttvvtttq",
+"qqqqrqqovvqqvqttttttttttqqqvqqoroonnppmmmmmjjjjjjmmmmmpmpprroovqqqvqvvtttqqvtqqq",
+"qqvoqqqoorqqqqtttttttwttvttvqqoqoooonppppnmmmlmmmmmmmmpppppppoorrroooqqqqqqqqqqq",
+"qorroorrrrqrqqvvtttvtttvttvvvvqvqooropprppppmlmmmmmmmplmnpnpnpopprrorrroorqooqro",
+"rrrrrrnnoroorqrqvvqtttvttvttvvtqvvqqoorrpnnlplmpnpmpmmlmppppnllpoopproorrrroroor",
+"prpnnnpppnnpnprooroqovqqvvqqqvqvqqrqroponrplmpnmmmlllmmmpmmpplpplmppnrlpprnonnnp",
+"pmlmlmpmlmmnlppppnoooooooqqvqqqqrqrrqroonlppllnnlmnlmmnmnmmnllmmlpmmpmpppnnpplpp",
+"mmmlmlllkkkkkkjjjmlmppnppooorqoroorrornonnppppnmpmmmmmmmmmmmmllllmmmmmmllmnlmmlm",
+"kkkkkkkkkkhhhhhkkkkkjlmmpppnnnnrroorrrrrolplpmnmmmlllmjlkmljmllkkkljjklkkllllkkk",
+"kkkhkhhiiiiihhhhhhhkkjkjjmllpppnpppnnonnnnpnpmmmlkljjjjjlkkmkkkkkjjkkkkkkjkkkkjk",
+"hhhhiiiiiiiigggegieeihhhkkkkmmmllpnnplolpnlnnmlklkklkkkkkkkkkkkkkhhhkkkkhhkhhhhh",
+"hhhhiefgeeeeceeeeeeeeefhhhkkkjjlmmmmplllnmnmllkkkklkkkkkkkhkkkhkhhhkhhhhhhiihhhf",
+"gieieegggdeecdeceeeeeeeeeihhhkkkkkmmmmmmmmmmljmjlkkkkhhhkkhhkkkhhhhhhhfffhfhhiih",
+"geggeeeeedceebcccbcgeegeiehhhkkkjjmllmmmmmmmmmjjllkkjkkhkhkhhkhhhhihhieiiheieeif",
+"igeeeeeeccceddbbbcbcceeefehhhhkkkjkklmmlmlmmmmmmjlllkkkkkkhhkhhhhhhhhfheeifgfege",
+"gggegggggbcccbcccccccegfihhihkhkklllmlmlmlmlmpmmlmmmjkkkkkkhkhhhhhhefhihifggegeg",
+"eggeggggggeeeeeeedecgeegehhhhhkkkklllmmnmlnlmnmpmlmmlkllkjjkkkkkkhhheeeieehfeege",
+"ggeeggggggeeeeegggggeeefehhhhhhkklkllmpmpnnnppppnlmplmmmmmkllkkkhhhhhhhfhihfegee",
+"ggifffhhiiiiggeegegfheeeeifihhkkkkklnmmppppppnnppnnnmpnplmlmmmmjkkkkhhihhhhiffif"
+};