aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfluxgen <fluxgen>2006-04-23 17:00:33 (GMT)
committerfluxgen <fluxgen>2006-04-23 17:00:33 (GMT)
commit75d289be65e12ac8c1b36a21ee9db8fee7cf1897 (patch)
tree14ce0593761c5c3e1f331563655bd5af7d127be6
parentb0d7010f8bd762aa3eff5ee5a9d5d265ad349c83 (diff)
downloadfluxbox-75d289be65e12ac8c1b36a21ee9db8fee7cf1897.zip
fluxbox-75d289be65e12ac8c1b36a21ee9db8fee7cf1897.tar.bz2
added support for _NET_WM_ALLOWED_ACTIONS and all the _NET_WM_ACTION_*. Added _NET_WM_STATE_MODAL and _NET_WM_WINDOW_TYPE_SPLASH
-rw-r--r--src/Ewmh.cc149
-rw-r--r--src/Ewmh.hh4
-rw-r--r--src/Makefile.am1
-rw-r--r--src/WinClientUtil.cc32
-rw-r--r--src/WinClientUtil.hh19
-rw-r--r--src/Window.hh1
6 files changed, 178 insertions, 28 deletions
diff --git a/src/Ewmh.cc b/src/Ewmh.cc
index fc762e7..6f12ab7 100644
--- a/src/Ewmh.cc
+++ b/src/Ewmh.cc
@@ -28,6 +28,8 @@
28#include "WinClient.hh" 28#include "WinClient.hh"
29#include "Workspace.hh" 29#include "Workspace.hh"
30#include "Layer.hh" 30#include "Layer.hh"
31#include "WinClientUtil.hh"
32
31#include "FbTk/App.hh" 33#include "FbTk/App.hh"
32#include "FbTk/FbWindow.hh" 34#include "FbTk/FbWindow.hh"
33#include "FbTk/I18n.hh" 35#include "FbTk/I18n.hh"
@@ -109,11 +111,28 @@ void Ewmh::initForScreen(BScreen &screen) {
109 m_net_wm_state_fullscreen, 111 m_net_wm_state_fullscreen,
110 m_net_wm_state_hidden, 112 m_net_wm_state_hidden,
111 m_net_wm_state_skip_taskbar, 113 m_net_wm_state_skip_taskbar,
112 114 m_net_wm_state_modal,
115 m_net_wm_state_below,
116 m_net_wm_state_above,
117
113 // window type 118 // window type
114 m_net_wm_window_type, 119 m_net_wm_window_type,
115 m_net_wm_window_type_dock, 120 m_net_wm_window_type_dock,
116 m_net_wm_window_type_desktop, 121 m_net_wm_window_type_desktop,
122 m_net_wm_window_type_splash,
123
124 // window actions
125 m_net_wm_allowed_actions,
126 m_net_wm_action_move,
127 m_net_wm_action_resize,
128 m_net_wm_action_minimize,
129 m_net_wm_action_shade,
130 m_net_wm_action_stick,
131 m_net_wm_action_maximize_horz,
132 m_net_wm_action_maximize_vert,
133 m_net_wm_action_fullscreen,
134 m_net_wm_action_change_desktop,
135 m_net_wm_action_close,
117 136
118 // root properties 137 // root properties
119 m_net_client_list, 138 m_net_client_list,
@@ -223,6 +242,15 @@ void Ewmh::setupFrame(FluxboxWindow &win) {
223 win.setMovable(false); 242 win.setMovable(false);
224 win.setResizable(false); 243 win.setResizable(false);
225 win.stick(); 244 win.stick();
245
246 } else if (atoms[l] == m_net_wm_window_type_splash) {
247 /*
248 * _NET_WM_WINDOW_TYPE_SPLASH indicates that the
249 * window is a splash screen displayed as an application
250 * is starting up.
251 */
252 win.setDecoration(FluxboxWindow::DECOR_NONE);
253 win.setMovable(false);
226 } 254 }
227 255
228 } 256 }
@@ -297,7 +325,8 @@ void Ewmh::updateClientList(BScreen &screen) {
297 Window *wl = FB_new_nothrow Window[num]; 325 Window *wl = FB_new_nothrow Window[num];
298 if (wl == 0) { 326 if (wl == 0) {
299 _FB_USES_NLS; 327 _FB_USES_NLS;
300 cerr<<_FBTEXT(Ewmh, OutOfMemoryClientList, "Fatal: Out of memory, can't allocate for EWMH client list", "")<<endl; 328 cerr<<_FBTEXT(Ewmh, OutOfMemoryClientList,
329 "Fatal: Out of memory, can't allocate for EWMH client list", "")<<endl;
301 return; 330 return;
302 } 331 }
303 332
@@ -523,8 +552,8 @@ void Ewmh::updateWorkarea(BScreen &screen) {
523 552
524void Ewmh::updateState(FluxboxWindow &win) { 553void Ewmh::updateState(FluxboxWindow &win) {
525 554
526 // TODO: should we update the _NET_WM_ALLOWED_ACTIONS 555
527 // here too? 556 updateActions(win);
528 557
529 typedef std::vector<unsigned int> StateVec; 558 typedef std::vector<unsigned int> StateVec;
530 559
@@ -542,14 +571,13 @@ void Ewmh::updateState(FluxboxWindow &win) {
542 state.push_back(m_net_wm_state_hidden); 571 state.push_back(m_net_wm_state_hidden);
543 if (win.isIconHidden()) 572 if (win.isIconHidden())
544 state.push_back(m_net_wm_state_skip_taskbar); 573 state.push_back(m_net_wm_state_skip_taskbar);
545 if (win.isFullscreen()) { 574 if (win.isFullscreen())
546 state.push_back(m_net_wm_state_fullscreen); 575 state.push_back(m_net_wm_state_fullscreen);
547 } 576 if (win.winClient().isModal())
577 state.push_back(m_net_wm_state_modal);
548 578
549 FluxboxWindow::ClientList::iterator it = win.clientList().begin(); 579 FluxboxWindow::ClientList::iterator it = win.clientList().begin();
550 FluxboxWindow::ClientList::iterator it_end = win.clientList().end(); 580 FluxboxWindow::ClientList::iterator it_end = win.clientList().end();
551
552 it = win.clientList().begin();
553 for (; it != it_end; ++it) { 581 for (; it != it_end; ++it) {
554 582
555 // search the old states for _NET_WM_STATE_SKIP_PAGER and append it 583 // search the old states for _NET_WM_STATE_SKIP_PAGER and append it
@@ -565,18 +593,19 @@ void Ewmh::updateState(FluxboxWindow &win) {
565 &data); 593 &data);
566 if (data) { 594 if (data) {
567 Atom *old_states = (Atom *)data; 595 Atom *old_states = (Atom *)data;
568 for (unsigned long i=0; i < nitems; ++i) 596 for (unsigned long i=0; i < nitems; ++i) {
569 if (old_states[i] == m_net_wm_state_skip_pager) { 597 if (old_states[i] == m_net_wm_state_skip_pager) {
570 client_state.push_back(m_net_wm_state_skip_pager); 598 client_state.push_back(m_net_wm_state_skip_pager);
571 } 599 }
600 }
572 XFree(data); 601 XFree(data);
573 } 602 }
574 603
575 if (!client_state.empty()) 604 if (!client_state.empty()) {
576 (*it)->changeProperty(m_net_wm_state, XA_ATOM, 32, PropModeReplace, 605 (*it)->changeProperty(m_net_wm_state, XA_ATOM, 32, PropModeReplace,
577 reinterpret_cast<unsigned char*>(&client_state.front()), 606 reinterpret_cast<unsigned char*>(&client_state.front()),
578 client_state.size()); 607 client_state.size());
579 else 608 } else
580 (*it)->deleteProperty(m_net_wm_state); 609 (*it)->deleteProperty(m_net_wm_state);
581 } 610 }
582} 611}
@@ -750,9 +779,6 @@ bool Ewmh::checkClientMessage(const XClientMessageEvent &ce,
750 779
751bool Ewmh::propertyNotify(WinClient &winclient, Atom the_property) { 780bool Ewmh::propertyNotify(WinClient &winclient, Atom the_property) {
752 if (the_property == m_net_wm_strut) { 781 if (the_property == m_net_wm_strut) {
753#ifdef DEBUG
754 cerr<<"_NET_WM_STRUT"<<endl;
755#endif // DEBUG
756 updateStrut(winclient); 782 updateStrut(winclient);
757 return true; 783 return true;
758 } 784 }
@@ -791,6 +817,7 @@ void Ewmh::createAtoms() {
791 m_net_wm_window_type = XInternAtom(disp, "_NET_WM_WINDOW_TYPE", False); 817 m_net_wm_window_type = XInternAtom(disp, "_NET_WM_WINDOW_TYPE", False);
792 m_net_wm_window_type_dock = XInternAtom(disp, "_NET_WM_WINDOW_TYPE_DOCK", False); 818 m_net_wm_window_type_dock = XInternAtom(disp, "_NET_WM_WINDOW_TYPE_DOCK", False);
793 m_net_wm_window_type_desktop = XInternAtom(disp, "_NET_WM_WINDOW_TYPE_DESKTOP", False); 819 m_net_wm_window_type_desktop = XInternAtom(disp, "_NET_WM_WINDOW_TYPE_DESKTOP", False);
820 m_net_wm_window_type_splash = XInternAtom(disp, "_NET_WM_WINDOW_TYPE_SPLASH", False);
794 821
795 // state atom and the supported state atoms 822 // state atom and the supported state atoms
796 m_net_wm_state = XInternAtom(disp, "_NET_WM_STATE", False); 823 m_net_wm_state = XInternAtom(disp, "_NET_WM_STATE", False);
@@ -804,19 +831,20 @@ void Ewmh::createAtoms() {
804 m_net_wm_state_skip_pager = XInternAtom(disp, "_NET_WM_STATE_SKIP_PAGER", False); 831 m_net_wm_state_skip_pager = XInternAtom(disp, "_NET_WM_STATE_SKIP_PAGER", False);
805 m_net_wm_state_above = XInternAtom(disp, "_NET_WM_STATE_ABOVE", False); 832 m_net_wm_state_above = XInternAtom(disp, "_NET_WM_STATE_ABOVE", False);
806 m_net_wm_state_below = XInternAtom(disp, "_NET_WM_STATE_BELOW", False); 833 m_net_wm_state_below = XInternAtom(disp, "_NET_WM_STATE_BELOW", False);
834 m_net_wm_state_modal = XInternAtom(disp, "_NET_WM_STATE_MODAL", False);
807 835
808 // allowed actions 836 // allowed actions
809 m_net_wm_allowed_actions = XInternAtom(disp, "_NET_WM_ALLOWED_ACTIONS", False); 837 m_net_wm_allowed_actions = XInternAtom(disp, "_NET_WM_ALLOWED_ACTIONS", False);
810 m_net_wm_action_move = XInternAtom(disp, "_NET_WM_ACTIONS_MOVE", False); 838 m_net_wm_action_move = XInternAtom(disp, "_NET_WM_ACTION_MOVE", False);
811 m_net_wm_action_resize = XInternAtom(disp, "_NET_WM_ACTIONS_RESIZE", False); 839 m_net_wm_action_resize = XInternAtom(disp, "_NET_WM_ACTION_RESIZE", False);
812 m_net_wm_action_minimize = XInternAtom(disp, "_NET_WM_ACTIONS_MINIMIZE", False); 840 m_net_wm_action_minimize = XInternAtom(disp, "_NET_WM_ACTION_MINIMIZE", False);
813 m_net_wm_action_shade = XInternAtom(disp, "_NET_WM_ACTIONS_SHADE", False); 841 m_net_wm_action_shade = XInternAtom(disp, "_NET_WM_ACTION_SHADE", False);
814 m_net_wm_action_stick = XInternAtom(disp, "_NET_WM_ACTIONS_STICK", False); 842 m_net_wm_action_stick = XInternAtom(disp, "_NET_WM_ACTION_STICK", False);
815 m_net_wm_action_maximize_horz = XInternAtom(disp, "_NET_WM_ACTIONS_MAXIMIZE_HORZ", False); 843 m_net_wm_action_maximize_horz = XInternAtom(disp, "_NET_WM_ACTION_MAXIMIZE_HORZ", False);
816 m_net_wm_action_maximize_vert = XInternAtom(disp, "_NET_WM_ACTIONS_MAXIMIZE_VERT", False); 844 m_net_wm_action_maximize_vert = XInternAtom(disp, "_NET_WM_ACTION_MAXIMIZE_VERT", False);
817 m_net_wm_action_fullscreen = XInternAtom(disp, "_NET_WM_ACTIONS_FULLSCREEN", False); 845 m_net_wm_action_fullscreen = XInternAtom(disp, "_NET_WM_ACTION_FULLSCREEN", False);
818 m_net_wm_action_change_desktop = XInternAtom(disp, "_NET_WM_ACTIONS_CHANGE_DESKTOP", False); 846 m_net_wm_action_change_desktop = XInternAtom(disp, "_NET_WM_ACTION_CHANGE_DESKTOP", False);
819 m_net_wm_action_close = XInternAtom(disp, "_NET_WM_ACTIONS_CLOSE", False); 847 m_net_wm_action_close = XInternAtom(disp, "_NET_WM_ACTION_CLOSE", False);
820 848
821 m_net_wm_strut = XInternAtom(disp, "_NET_WM_STRUT", False); 849 m_net_wm_strut = XInternAtom(disp, "_NET_WM_STRUT", False);
822 m_net_wm_icon_geometry = XInternAtom(disp, "_NET_WM_ICON_GEOMETRY", False); 850 m_net_wm_icon_geometry = XInternAtom(disp, "_NET_WM_ICON_GEOMETRY", False);
@@ -890,7 +918,7 @@ void Ewmh::setState(FluxboxWindow &win, Atom state, bool value) {
890 win.iconify(); 918 win.iconify();
891 else if (!value && win.isIconic()) 919 else if (!value && win.isIconic())
892 win.deiconify(); 920 win.deiconify();
893 } else if (state == m_net_wm_state_skip_taskbar) { 921 } else if (state == m_net_wm_state_skip_taskbar) { // skip taskbar
894 win.setIconHidden(value); 922 win.setIconHidden(value);
895 } else if (state == m_net_wm_state_below) { // bottom layer 923 } else if (state == m_net_wm_state_below) { // bottom layer
896 if (value) 924 if (value)
@@ -904,13 +932,14 @@ void Ewmh::setState(FluxboxWindow &win, Atom state, bool value) {
904 else 932 else
905 win.moveToLayer(Layer::NORMAL); 933 win.moveToLayer(Layer::NORMAL);
906 } 934 }
935 // Note: state == net_wm_state_modal, We should not change it
907} 936}
908 937
909// toggle window state 938// toggle window state
910void Ewmh::toggleState(FluxboxWindow &win, Atom state) { 939void Ewmh::toggleState(FluxboxWindow &win, Atom state) {
911 if (state == m_net_wm_state_sticky) { 940 if (state == m_net_wm_state_sticky) { // sticky
912 win.stick(); 941 win.stick();
913 } else if (state == m_net_wm_state_shaded){ 942 } else if (state == m_net_wm_state_shaded){ // shaded
914 win.shade(); 943 win.shade();
915 } else if (state == m_net_wm_state_maximized_horz ) { // maximized Horizontal 944 } else if (state == m_net_wm_state_maximized_horz ) { // maximized Horizontal
916 win.maximizeHorizontal(); 945 win.maximizeHorizontal();
@@ -923,7 +952,7 @@ void Ewmh::toggleState(FluxboxWindow &win, Atom state) {
923 win.deiconify(); 952 win.deiconify();
924 else 953 else
925 win.iconify(); 954 win.iconify();
926 } else if (state == m_net_wm_state_skip_taskbar) { 955 } else if (state == m_net_wm_state_skip_taskbar) { // taskbar
927 win.setIconHidden(!win.isIconHidden()); 956 win.setIconHidden(!win.isIconHidden());
928 } else if (state == m_net_wm_state_below) { // bottom layer 957 } else if (state == m_net_wm_state_below) { // bottom layer
929 if (win.layerNum() == Layer::BOTTOM) 958 if (win.layerNum() == Layer::BOTTOM)
@@ -958,6 +987,70 @@ void Ewmh::updateStrut(WinClient &winclient) {
958 } 987 }
959} 988}
960 989
990void Ewmh::updateActions(FluxboxWindow &win) {
991
992 /* From Extended Window Manager Hints, draft 1.3:
993 *
994 * _NET_WM_ALLOWED_ACTIONS, ATOM[]
995 *
996 * A list of atoms indicating user operations that the
997 * Window Manager supports for this window. Atoms present in the
998 * list indicate allowed actions, atoms not present in the list
999 * indicate actions that are not supported for this window. The
1000 * Window Manager MUST keep this property updated to reflect the
1001 * actions which are currently "active" or "sensitive" for a window.
1002 * Taskbars, Pagers, and other tools use _NET_WM_ALLOWED_ACTIONS to
1003 * decide which actions should be made available to the user.
1004 */
1005
1006 typedef std::vector<Atom> ActionsVector;
1007 ActionsVector actions;
1008 actions.reserve(10);
1009 // all windows can change desktop,
1010 // be shaded or be sticky
1011 actions.push_back(m_net_wm_action_change_desktop);
1012 actions.push_back(m_net_wm_action_shade);
1013 actions.push_back(m_net_wm_action_stick);
1014
1015 if (win.isResizable())
1016 actions.push_back(m_net_wm_action_resize);
1017 if (win.isMoveable())
1018 actions.push_back(m_net_wm_action_move);
1019 if (win.isClosable())
1020 actions.push_back(m_net_wm_action_close);
1021 if (win.isIconifiable())
1022 actions.push_back(m_net_wm_action_minimize);
1023
1024 unsigned int max_width, max_height;
1025 WinClientUtil::maxSize(win.clientList(), max_width, max_height);
1026
1027 // if unlimited max width we can maximize horizontal
1028 if (max_width == 0) {
1029 actions.push_back(m_net_wm_action_maximize_horz);
1030 }
1031 // if unlimited max height we can maxmize vert
1032 if (max_height == 0) {
1033 actions.push_back(m_net_wm_action_maximize_vert);
1034 }
1035
1036 // if we have unlimited size in all directions we can have this window
1037 // in fullscreen mode
1038 if (max_height == 0 && max_width == 0) {
1039 actions.push_back(m_net_wm_action_fullscreen);
1040 }
1041
1042
1043
1044 FluxboxWindow::ClientList::iterator it = win.clientList().begin();
1045 FluxboxWindow::ClientList::iterator it_end = win.clientList().end();
1046 for (; it != it_end; ++it) {
1047 (*it)->changeProperty(m_net_wm_allowed_actions, XA_ATOM, 32, PropModeReplace,
1048 reinterpret_cast<unsigned char*>(&actions.front()),
1049 actions.size());
1050 }
1051
1052}
1053
961void Ewmh::setupState(FluxboxWindow &win) { 1054void Ewmh::setupState(FluxboxWindow &win) {
962 /* From Extended Window Manager Hints, draft 1.3: 1055 /* From Extended Window Manager Hints, draft 1.3:
963 * 1056 *
diff --git a/src/Ewmh.hh b/src/Ewmh.hh
index 59c9877..b1f5d39 100644
--- a/src/Ewmh.hh
+++ b/src/Ewmh.hh
@@ -77,6 +77,8 @@ private:
77 void toggleState(FluxboxWindow &win, Atom state); 77 void toggleState(FluxboxWindow &win, Atom state);
78 void createAtoms(); 78 void createAtoms();
79 void updateStrut(WinClient &winclient); 79 void updateStrut(WinClient &winclient);
80 void updateActions(FluxboxWindow &win);
81
80 void setupState(FluxboxWindow &win); 82 void setupState(FluxboxWindow &win);
81 83
82 // root window properties 84 // root window properties
@@ -94,6 +96,7 @@ private:
94 m_net_wm_window_type, 96 m_net_wm_window_type,
95 m_net_wm_window_type_dock, 97 m_net_wm_window_type_dock,
96 m_net_wm_window_type_desktop, 98 m_net_wm_window_type_desktop,
99 m_net_wm_window_type_splash,
97 100
98 // states 101 // states
99 m_net_wm_state, m_net_wm_state_sticky, m_net_wm_state_shaded, 102 m_net_wm_state, m_net_wm_state_sticky, m_net_wm_state_shaded,
@@ -104,6 +107,7 @@ private:
104 m_net_wm_state_skip_pager, 107 m_net_wm_state_skip_pager,
105 m_net_wm_state_below, 108 m_net_wm_state_below,
106 m_net_wm_state_above, 109 m_net_wm_state_above,
110 m_net_wm_state_modal,
107 111
108 // allowed actions 112 // allowed actions
109 m_net_wm_allowed_actions, 113 m_net_wm_allowed_actions,
diff --git a/src/Makefile.am b/src/Makefile.am
index 38a3b39..890c06c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -135,6 +135,7 @@ fluxbox_SOURCES = AtomHandler.hh ArrowButton.hh ArrowButton.cc \
135 RowSmartPlacement.hh RowSmartPlacement.cc \ 135 RowSmartPlacement.hh RowSmartPlacement.cc \
136 ScreenPlacement.hh ScreenPlacement.cc \ 136 ScreenPlacement.hh ScreenPlacement.cc \
137 UnderMousePlacement.hh UnderMousePlacement.cc \ 137 UnderMousePlacement.hh UnderMousePlacement.cc \
138 WinClientUtil.hh WinClientUtil.cc \
138 ${newwmspec_SOURCE} ${gnome_SOURCE} \ 139 ${newwmspec_SOURCE} ${gnome_SOURCE} \
139 ${REMEMBER_SOURCE} ${TOOLBAR_SOURCE} 140 ${REMEMBER_SOURCE} ${TOOLBAR_SOURCE}
140 141
diff --git a/src/WinClientUtil.cc b/src/WinClientUtil.cc
new file mode 100644
index 0000000..112360d
--- /dev/null
+++ b/src/WinClientUtil.cc
@@ -0,0 +1,32 @@
1#include "WinClientUtil.hh"
2#include "WinClient.hh"
3
4#include <algorithm>
5
6namespace WinClientUtil {
7
8void maxSize(const FluxboxWindow::ClientList &clients,
9 unsigned int &max_width, unsigned int &max_height) {
10 FluxboxWindow::ClientList::const_iterator it = clients.begin();
11 FluxboxWindow::ClientList::const_iterator it_end = clients.end();
12 max_width = ~0; // unlimited
13 max_height = ~0; // unlimited
14 for (; it != it_end; ++it) {
15 // special case for max height/width == 0
16 // 0 indicates unlimited size, so we skip them
17 // and set max size to 0 if max size == ~0 after the loop
18 if ((*it)->maxHeight() != 0)
19 max_height = std::min( (*it)->maxHeight(), max_height );
20 if ((*it)->maxWidth() != 0)
21 max_width = std::min( (*it)->maxWidth(), max_width );
22 }
23
24 if (max_width == ~0)
25 max_width = 0;
26 if (max_height == ~0)
27 max_height = 0;
28}
29
30}
31
32
diff --git a/src/WinClientUtil.hh b/src/WinClientUtil.hh
new file mode 100644
index 0000000..8ad0a03
--- /dev/null
+++ b/src/WinClientUtil.hh
@@ -0,0 +1,19 @@
1#ifndef WINCLIENTUTIL_H
2#define WINCLIENTUTIL_H
3
4#include "Window.hh"
5
6/// window client utilities
7namespace WinClientUtil {
8
9/**
10 * Calculates the min of all maximum width/heights of all clients
11 * @param clients the client list
12 * @param width the return value of minimum of all max width of all clients
13 * @param height the return value of mimimum of all max heights of all clients
14 */
15void maxSize(const FluxboxWindow::ClientList &clients,
16 unsigned int &width, unsigned int &height);
17}
18
19#endif // WINCLIENTUTIL_H
diff --git a/src/Window.hh b/src/Window.hh
index 9508d5c..10491ff 100644
--- a/src/Window.hh
+++ b/src/Window.hh
@@ -302,6 +302,7 @@ public:
302 inline bool isMaximizable() const { return functions.maximize; } 302 inline bool isMaximizable() const { return functions.maximize; }
303 inline bool isResizable() const { return functions.resize; } 303 inline bool isResizable() const { return functions.resize; }
304 inline bool isClosable() const { return functions.close; } 304 inline bool isClosable() const { return functions.close; }
305 inline bool isMoveable() const { return functions.move; }
305 inline bool isStuck() const { return stuck; } 306 inline bool isStuck() const { return stuck; }
306 inline bool hasTitlebar() const { return decorations.titlebar; } 307 inline bool hasTitlebar() const { return decorations.titlebar; }
307 inline bool isMoving() const { return moving; } 308 inline bool isMoving() const { return moving; }