diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/fluxbox.cc | 416 |
1 files changed, 25 insertions, 391 deletions
diff --git a/src/fluxbox.cc b/src/fluxbox.cc index b4c056f..e81fc52 100644 --- a/src/fluxbox.cc +++ b/src/fluxbox.cc | |||
@@ -22,7 +22,7 @@ | |||
22 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 22 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
23 | // DEALINGS IN THE SOFTWARE. | 23 | // DEALINGS IN THE SOFTWARE. |
24 | 24 | ||
25 | // $Id: fluxbox.cc,v 1.164 2003/06/25 13:06:04 fluxgen Exp $ | 25 | // $Id: fluxbox.cc,v 1.165 2003/06/30 15:05:26 fluxgen Exp $ |
26 | 26 | ||
27 | #include "fluxbox.hh" | 27 | #include "fluxbox.hh" |
28 | 28 | ||
@@ -408,6 +408,8 @@ Fluxbox::Fluxbox(int argc, char **argv, const char *dpy_name, const char *rcfile | |||
408 | m_rc_cache_max(m_resourcemanager, 200, "session.cacheMax", "Session.CacheMax"), | 408 | m_rc_cache_max(m_resourcemanager, 200, "session.cacheMax", "Session.CacheMax"), |
409 | m_focused_window(0), m_masked_window(0), | 409 | m_focused_window(0), m_masked_window(0), |
410 | m_timer(this), | 410 | m_timer(this), |
411 | m_mousescreen(0), | ||
412 | m_keyscreen(0), | ||
411 | m_watching_screen(0), m_watch_keyrelease(0), | 413 | m_watching_screen(0), m_watch_keyrelease(0), |
412 | m_last_time(0), | 414 | m_last_time(0), |
413 | m_masked(0), | 415 | m_masked(0), |
@@ -541,6 +543,7 @@ Fluxbox::Fluxbox(int argc, char **argv, const char *dpy_name, const char *rcfile | |||
541 | m_atomhandler[atomh]->initForScreen(*screen); | 543 | m_atomhandler[atomh]->initForScreen(*screen); |
542 | } | 544 | } |
543 | } | 545 | } |
546 | m_keyscreen = m_mousescreen = m_screen_list.front(); | ||
544 | 547 | ||
545 | if (m_screen_list.size() == 0) { | 548 | if (m_screen_list.size() == 0) { |
546 | //!! TODO: NLS | 549 | //!! TODO: NLS |
@@ -715,6 +718,7 @@ void Fluxbox::setupConfigFiles() { | |||
715 | } | 718 | } |
716 | 719 | ||
717 | void Fluxbox::handleEvent(XEvent * const e) { | 720 | void Fluxbox::handleEvent(XEvent * const e) { |
721 | m_last_event = *e; | ||
718 | 722 | ||
719 | // it is possible (e.g. during moving) for a window | 723 | // it is possible (e.g. during moving) for a window |
720 | // to mask all events to go to it | 724 | // to mask all events to go to it |
@@ -902,7 +906,8 @@ void Fluxbox::handleEvent(XEvent * const e) { | |||
902 | } | 906 | } |
903 | 907 | ||
904 | void Fluxbox::handleButtonEvent(XButtonEvent &be) { | 908 | void Fluxbox::handleButtonEvent(XButtonEvent &be) { |
905 | 909 | m_mousescreen = searchScreen(be.root); | |
910 | |||
906 | switch (be.type) { | 911 | switch (be.type) { |
907 | case ButtonPress: { | 912 | case ButtonPress: { |
908 | m_last_time = be.time; | 913 | m_last_time = be.time; |
@@ -1095,276 +1100,25 @@ void Fluxbox::handleClientMessage(XClientMessageEvent &ce) { | |||
1095 | Handles KeyRelease and KeyPress events | 1100 | Handles KeyRelease and KeyPress events |
1096 | */ | 1101 | */ |
1097 | void Fluxbox::handleKeyEvent(XKeyEvent &ke) { | 1102 | void Fluxbox::handleKeyEvent(XKeyEvent &ke) { |
1098 | switch (ke.type) { | 1103 | m_keyscreen = searchScreen(ke.window); |
1099 | case KeyPress: { | 1104 | |
1100 | BScreen *keyscreen = searchScreen(ke.window); | 1105 | m_mousescreen = keyScreen(); |
1101 | 1106 | Window root, ignorew; | |
1102 | BScreen *mousescreen = keyscreen; | 1107 | int ignored; |
1103 | Window root, ignorew; | 1108 | if (!XQueryPointer(FbTk::App::instance()->display(), |
1104 | int ignored; | 1109 | ke.window, &root, &ignorew, &ignored, &ignored, |
1105 | if (!XQueryPointer(FbTk::App::instance()->display(), | 1110 | &ignored, &ignored, (unsigned int *)&ignored)) |
1106 | ke.window, &root, &ignorew, &ignored, &ignored, | 1111 | // pointer on different screen to ke.window |
1107 | &ignored, &ignored, (unsigned int *)&ignored)) | 1112 | m_mousescreen = searchScreen(root); |
1108 | // pointer on different screen to ke.window | 1113 | |
1109 | mousescreen = searchScreen(root); | 1114 | if (keyScreen() == 0 || mouseScreen() == 0) |
1110 | 1115 | return; | |
1111 | if (keyscreen == 0 || mousescreen == 0) | ||
1112 | break; | ||
1113 | |||
1114 | #ifdef DEBUG | ||
1115 | cerr<<__FILE__<<"("<<__FUNCTION__<<"): KeyEvent"<<endl; | ||
1116 | #endif | ||
1117 | //find action | ||
1118 | Keys::KeyAction action = m_key->getAction(&ke); | ||
1119 | #ifdef DEBUG | ||
1120 | const char *actionstr = m_key->getActionStr(action); | ||
1121 | if (actionstr) | ||
1122 | cerr<<"KeyAction("<<actionstr<<")"<<endl; | ||
1123 | #endif | ||
1124 | if (action==Keys::LASTKEYGRAB) //if action not found end case | ||
1125 | break; | ||
1126 | |||
1127 | // what to allow if moving | ||
1128 | if (m_focused_window && m_focused_window->isMoving()) { | ||
1129 | int allowed = false; | ||
1130 | switch (action) { | ||
1131 | case Keys::WORKSPACE: | ||
1132 | case Keys::SENDTOWORKSPACE: | ||
1133 | case Keys::WORKSPACE1: | ||
1134 | case Keys::WORKSPACE2: | ||
1135 | case Keys::WORKSPACE3: | ||
1136 | case Keys::WORKSPACE4: | ||
1137 | case Keys::WORKSPACE5: | ||
1138 | case Keys::WORKSPACE6: | ||
1139 | case Keys::WORKSPACE7: | ||
1140 | case Keys::WORKSPACE8: | ||
1141 | case Keys::WORKSPACE9: | ||
1142 | case Keys::WORKSPACE10: | ||
1143 | case Keys::WORKSPACE11: | ||
1144 | case Keys::WORKSPACE12: | ||
1145 | case Keys::NEXTWORKSPACE: | ||
1146 | case Keys::PREVWORKSPACE: | ||
1147 | case Keys::LEFTWORKSPACE: | ||
1148 | case Keys::RIGHTWORKSPACE: | ||
1149 | allowed = true; | ||
1150 | break; | ||
1151 | default: | ||
1152 | allowed = false; | ||
1153 | } | ||
1154 | if (!allowed) break; | ||
1155 | } | ||
1156 | |||
1157 | switch (action) { | ||
1158 | case Keys::WORKSPACE: | ||
1159 | // Workspace1 has id 0, hence -1 | ||
1160 | mousescreen->changeWorkspaceID(m_key->getParam()-1); | ||
1161 | break; | ||
1162 | case Keys::SENDTOWORKSPACE: | ||
1163 | // Workspace1 has id 0, hence -1 | ||
1164 | keyscreen->sendToWorkspace(m_key->getParam()-1); | ||
1165 | break; | ||
1166 | // NOTE!!! The WORKSPACEn commands are not needed anymore | ||
1167 | case Keys::WORKSPACE1: | ||
1168 | mousescreen->changeWorkspaceID(0); | ||
1169 | break; | ||
1170 | case Keys::WORKSPACE2: | ||
1171 | mousescreen->changeWorkspaceID(1); | ||
1172 | break; | ||
1173 | case Keys::WORKSPACE3: | ||
1174 | mousescreen->changeWorkspaceID(2); | ||
1175 | break; | ||
1176 | case Keys::WORKSPACE4: | ||
1177 | mousescreen->changeWorkspaceID(3); | ||
1178 | break; | ||
1179 | case Keys::WORKSPACE5: | ||
1180 | mousescreen->changeWorkspaceID(4); | ||
1181 | break; | ||
1182 | case Keys::WORKSPACE6: | ||
1183 | mousescreen->changeWorkspaceID(5); | ||
1184 | break; | ||
1185 | case Keys::WORKSPACE7: | ||
1186 | mousescreen->changeWorkspaceID(6); | ||
1187 | break; | ||
1188 | case Keys::WORKSPACE8: | ||
1189 | mousescreen->changeWorkspaceID(7); | ||
1190 | break; | ||
1191 | case Keys::WORKSPACE9: | ||
1192 | mousescreen->changeWorkspaceID(8); | ||
1193 | break; | ||
1194 | case Keys::WORKSPACE10: | ||
1195 | mousescreen->changeWorkspaceID(9); | ||
1196 | break; | ||
1197 | case Keys::WORKSPACE11: | ||
1198 | mousescreen->changeWorkspaceID(10); | ||
1199 | break; | ||
1200 | case Keys::WORKSPACE12: | ||
1201 | mousescreen->changeWorkspaceID(11); | ||
1202 | break; | ||
1203 | case Keys::NEXTWORKSPACE: | ||
1204 | mousescreen->nextWorkspace(m_key->getParam()); | ||
1205 | break; | ||
1206 | case Keys::PREVWORKSPACE: | ||
1207 | mousescreen->prevWorkspace(m_key->getParam()); | ||
1208 | break; | ||
1209 | case Keys::LEFTWORKSPACE: | ||
1210 | mousescreen->leftWorkspace(m_key->getParam()); | ||
1211 | break; | ||
1212 | case Keys::RIGHTWORKSPACE: | ||
1213 | mousescreen->rightWorkspace(m_key->getParam()); | ||
1214 | break; | ||
1215 | case Keys::KILLWINDOW: //kill the current window | ||
1216 | if (m_focused_window) { | ||
1217 | XKillClient(FbTk::App::instance()->display(), | ||
1218 | m_focused_window->clientWindow()); | ||
1219 | } | ||
1220 | break; | ||
1221 | case Keys::NEXTGROUP: //activate next group (params set right in Keys) | ||
1222 | case Keys::NEXTWINDOW: { //activate next window | ||
1223 | unsigned int mods = Keys::cleanMods(ke.state); | ||
1224 | if (mousescreen == 0) | ||
1225 | break; | ||
1226 | if (mods == 0) { // can't stacked cycle unless there is a mod to grab | ||
1227 | mousescreen->nextFocus(m_key->getParam() | BScreen::CYCLELINEAR); | ||
1228 | break; | ||
1229 | } | ||
1230 | if (!m_watching_screen && !(m_key->getParam() & BScreen::CYCLELINEAR)) { | ||
1231 | // if stacked cycling, then set a watch for | ||
1232 | // the release of exactly these modifiers | ||
1233 | watchKeyRelease(*mousescreen, mods); | ||
1234 | } | ||
1235 | mousescreen->nextFocus(m_key->getParam()); | ||
1236 | break; | ||
1237 | } | ||
1238 | case Keys::PREVGROUP: //activate prev group (params set right in Keys) | ||
1239 | case Keys::PREVWINDOW: {//activate prev window | ||
1240 | unsigned int mods = Keys::cleanMods(ke.state); | ||
1241 | if (mousescreen == 0) | ||
1242 | break; | ||
1243 | if (mods == 0) { // can't stacked cycle unless there is a mod to grab | ||
1244 | mousescreen->prevFocus(m_key->getParam() | BScreen::CYCLELINEAR); | ||
1245 | break; | ||
1246 | } | ||
1247 | if (!m_watching_screen && !(m_key->getParam() & BScreen::CYCLELINEAR)) { | ||
1248 | // if stacked cycling, then set a watch for | ||
1249 | // the release of exactly these modifiers | ||
1250 | watchKeyRelease(*mousescreen, mods); | ||
1251 | } | ||
1252 | mousescreen->prevFocus(m_key->getParam()); | ||
1253 | break; | ||
1254 | } | ||
1255 | case Keys::FOCUSUP: | ||
1256 | if (m_focused_window) | ||
1257 | keyscreen->dirFocus(*m_focused_window, BScreen::FOCUSUP); | ||
1258 | break; | ||
1259 | case Keys::FOCUSDOWN: | ||
1260 | if (m_focused_window) | ||
1261 | keyscreen->dirFocus(*m_focused_window, BScreen::FOCUSDOWN); | ||
1262 | break; | ||
1263 | case Keys::FOCUSLEFT: | ||
1264 | if (m_focused_window) | ||
1265 | keyscreen->dirFocus(*m_focused_window, BScreen::FOCUSLEFT); | ||
1266 | break; | ||
1267 | case Keys::FOCUSRIGHT: | ||
1268 | if (m_focused_window) | ||
1269 | keyscreen->dirFocus(*m_focused_window, BScreen::FOCUSRIGHT); | ||
1270 | break; | ||
1271 | case Keys::NEXTTAB: | ||
1272 | if (m_focused_window && m_focused_window->numClients() > 1) | ||
1273 | m_focused_window->nextClient(); | ||
1274 | break; | ||
1275 | case Keys::PREVTAB: | ||
1276 | if (m_focused_window && m_focused_window->numClients() > 1) | ||
1277 | m_focused_window->prevClient(); | ||
1278 | |||
1279 | break; | ||
1280 | case Keys::FIRSTTAB: | ||
1281 | cerr<<"FIRSTTAB TODO!"<<endl; | ||
1282 | break; | ||
1283 | case Keys::LASTTAB: | ||
1284 | cerr<<"LASTTAB TODO!"<<endl; | ||
1285 | break; | ||
1286 | case Keys::MOVETABPREV: | ||
1287 | cerr<<"MOVETABPREV TODO!"<<endl; | ||
1288 | break; | ||
1289 | case Keys::MOVETABNEXT: | ||
1290 | cerr<<"MOVETABNEXT TODO!"<<endl; | ||
1291 | break; | ||
1292 | case Keys::ATTACHLAST: | ||
1293 | //!! just attach last window to focused window | ||
1294 | if (m_focused_window) { | ||
1295 | Workspace *space = keyscreen->currentWorkspace(); | ||
1296 | Workspace::Windows &wins = space->windowList(); | ||
1297 | if (wins.size() == 1) | ||
1298 | break; | ||
1299 | BScreen::FocusedWindows &fwins = keyscreen->getFocusedList(); | ||
1300 | BScreen::FocusedWindows::iterator it = fwins.begin(); | ||
1301 | for (; it != fwins.end(); ++it) { | ||
1302 | if ((*it)->fbwindow() != m_focused_window && | ||
1303 | (*it)->fbwindow()->workspaceNumber() == | ||
1304 | keyscreen->currentWorkspaceID()) { | ||
1305 | m_focused_window->attachClient(**it); | ||
1306 | break; | ||
1307 | } | ||
1308 | } | ||
1309 | } | ||
1310 | break; | ||
1311 | case Keys::DETACHCLIENT: | ||
1312 | if (m_focused_window) { | ||
1313 | m_focused_window->detachClient(m_focused_window->winClient()); | ||
1314 | } | ||
1315 | break; | ||
1316 | case Keys::EXECUTE: { //execute command on keypress | ||
1317 | FbCommands::ExecuteCmd cmd(m_key->getExecCommand(), mousescreen->screenNumber()); | ||
1318 | cmd.execute(); | ||
1319 | } break; | ||
1320 | case Keys::RECONFIGURE: | ||
1321 | reload_rc(); | ||
1322 | break; | ||
1323 | case Keys::RESTART: { | ||
1324 | FbCommands::RestartFluxboxCmd cmd(m_key->getExecCommand()); | ||
1325 | cmd.execute(); | ||
1326 | } break; | ||
1327 | case Keys::QUIT: | ||
1328 | shutdown(); | ||
1329 | break; | ||
1330 | case Keys::ROOTMENU: { //show root menu | ||
1331 | |||
1332 | //calculate placement of workspace menu | ||
1333 | //and show/hide it | ||
1334 | int mx = ke.x_root - | ||
1335 | (mousescreen->getRootmenu()->width() / 2); | ||
1336 | int my = ke.y_root - | ||
1337 | (mousescreen->getRootmenu()->titleHeight() / 2); | ||
1338 | |||
1339 | if (mx < 0) mx = 0; | ||
1340 | if (my < 0) my = 0; | ||
1341 | |||
1342 | if (mx + mousescreen->getRootmenu()->width() > mousescreen->width()) { | ||
1343 | mx = mousescreen->width() - | ||
1344 | mousescreen->getRootmenu()->width() - | ||
1345 | mousescreen->getRootmenu()->fbwindow().borderWidth(); | ||
1346 | } | ||
1347 | |||
1348 | if (my + mousescreen->getRootmenu()->height() > | ||
1349 | mousescreen->height()) { | ||
1350 | my = mousescreen->height() - | ||
1351 | mousescreen->getRootmenu()->height() - | ||
1352 | mousescreen->getRootmenu()->fbwindow().borderWidth(); | ||
1353 | } | ||
1354 | mousescreen->getRootmenu()->move(mx, my); | ||
1355 | |||
1356 | if (! mousescreen->getRootmenu()->isVisible()) { | ||
1357 | checkMenu(); | ||
1358 | mousescreen->getRootmenu()->show(); | ||
1359 | } | ||
1360 | 1116 | ||
1361 | } break; | 1117 | |
1362 | default: //try to see if its a window action | 1118 | switch (ke.type) { |
1363 | doWindowAction(action, m_key->getParam()); | 1119 | case KeyPress: |
1364 | } | 1120 | m_key->doAction(ke); |
1365 | 1121 | break; | |
1366 | |||
1367 | } break; | ||
1368 | case KeyRelease: { | 1122 | case KeyRelease: { |
1369 | // we ignore most key releases unless we need to use | 1123 | // we ignore most key releases unless we need to use |
1370 | // a release to stop something (e.g. window cycling). | 1124 | // a release to stop something (e.g. window cycling). |
@@ -1394,126 +1148,6 @@ void Fluxbox::handleKeyEvent(XKeyEvent &ke) { | |||
1394 | 1148 | ||
1395 | 1149 | ||
1396 | } | 1150 | } |
1397 | void Fluxbox::doWindowAction(int action, const int param) { | ||
1398 | if (!m_focused_window) | ||
1399 | return; | ||
1400 | |||
1401 | switch (action) { | ||
1402 | case Keys::ICONIFY: | ||
1403 | m_focused_window->iconify(); | ||
1404 | break; | ||
1405 | case Keys::RAISE: | ||
1406 | m_focused_window->raise(); | ||
1407 | break; | ||
1408 | case Keys::LOWER: | ||
1409 | m_focused_window->lower(); | ||
1410 | break; | ||
1411 | case Keys::RAISELAYER: | ||
1412 | m_focused_window->raiseLayer(); | ||
1413 | break; | ||
1414 | case Keys::LOWERLAYER: | ||
1415 | m_focused_window->lowerLayer(); | ||
1416 | break; | ||
1417 | case Keys::TOPLAYER: | ||
1418 | m_focused_window->moveToLayer(getBottomLayer()); | ||
1419 | break; | ||
1420 | case Keys::BOTTOMLAYER: | ||
1421 | m_focused_window->moveToLayer(getTopLayer()); | ||
1422 | break; | ||
1423 | case Keys::CLOSE: | ||
1424 | m_focused_window->close(); | ||
1425 | break; | ||
1426 | case Keys::SHADE: | ||
1427 | m_focused_window->shade(); // this has to be done in THIS order | ||
1428 | break; | ||
1429 | case Keys::MAXIMIZE: | ||
1430 | m_focused_window->maximize(); | ||
1431 | break; | ||
1432 | case Keys::STICK: | ||
1433 | m_focused_window->stick(); | ||
1434 | break; | ||
1435 | case Keys::VERTMAX: | ||
1436 | if (m_focused_window->isResizable()) | ||
1437 | m_focused_window->maximizeVertical(); | ||
1438 | break; | ||
1439 | case Keys::HORIZMAX: | ||
1440 | if (m_focused_window->isResizable()) | ||
1441 | m_focused_window->maximizeHorizontal(); | ||
1442 | break; | ||
1443 | case Keys::NUDGERIGHT: | ||
1444 | m_focused_window->moveResize( | ||
1445 | m_focused_window->x() + param, m_focused_window->y(), | ||
1446 | m_focused_window->width(), m_focused_window->height()); | ||
1447 | break; | ||
1448 | case Keys::NUDGELEFT: | ||
1449 | m_focused_window->moveResize( | ||
1450 | m_focused_window->x() - param, m_focused_window->y(), | ||
1451 | m_focused_window->width(), m_focused_window->height()); | ||
1452 | break; | ||
1453 | case Keys::NUDGEUP: | ||
1454 | m_focused_window->moveResize( | ||
1455 | m_focused_window->x(), m_focused_window->y() - param, | ||
1456 | m_focused_window->width(), m_focused_window->height()); | ||
1457 | break; | ||
1458 | case Keys::NUDGEDOWN: | ||
1459 | m_focused_window->moveResize( | ||
1460 | m_focused_window->x(), m_focused_window->y() + param, | ||
1461 | m_focused_window->width(), m_focused_window->height()); | ||
1462 | break; | ||
1463 | // NOTE !!! BIGNUDGExxxx is not needed, just use 10 as a parameter | ||
1464 | case Keys::BIGNUDGERIGHT: | ||
1465 | m_focused_window->moveResize( | ||
1466 | m_focused_window->x() + 10, m_focused_window->y(), | ||
1467 | m_focused_window->width(), m_focused_window->height()); | ||
1468 | break; | ||
1469 | case Keys::BIGNUDGELEFT: | ||
1470 | m_focused_window->moveResize( | ||
1471 | m_focused_window->x() - 10, m_focused_window->y(), | ||
1472 | m_focused_window->width(), m_focused_window->height()); | ||
1473 | break; | ||
1474 | case Keys::BIGNUDGEUP: | ||
1475 | m_focused_window->moveResize( | ||
1476 | m_focused_window->x(), m_focused_window->y()-10, | ||
1477 | m_focused_window->width(), m_focused_window->height()); | ||
1478 | break; | ||
1479 | case Keys::BIGNUDGEDOWN: | ||
1480 | m_focused_window->moveResize( | ||
1481 | m_focused_window->x(), m_focused_window->y()+10, | ||
1482 | m_focused_window->width(), m_focused_window->height()); | ||
1483 | break; | ||
1484 | case Keys::HORIZINC: | ||
1485 | m_focused_window->moveResize( | ||
1486 | m_focused_window->x(), m_focused_window->y(), | ||
1487 | m_focused_window->width() + 10, m_focused_window->height()); | ||
1488 | |||
1489 | break; | ||
1490 | case Keys::VERTINC: | ||
1491 | m_focused_window->moveResize( | ||
1492 | m_focused_window->x(), m_focused_window->y(), | ||
1493 | m_focused_window->width(), m_focused_window->height()+10); | ||
1494 | break; | ||
1495 | case Keys::HORIZDEC: | ||
1496 | m_focused_window->moveResize( | ||
1497 | m_focused_window->x(), m_focused_window->y(), | ||
1498 | m_focused_window->width() - 10, m_focused_window->height()); | ||
1499 | break; | ||
1500 | case Keys::VERTDEC: | ||
1501 | m_focused_window->moveResize( | ||
1502 | m_focused_window->x(), m_focused_window->y(), | ||
1503 | m_focused_window->width(), m_focused_window->height()-10); | ||
1504 | |||
1505 | break; | ||
1506 | case Keys::TOGGLEDECOR: | ||
1507 | m_focused_window->toggleDecoration(); | ||
1508 | break; | ||
1509 | case Keys::TOGGLETAB: | ||
1510 | cerr<<"TOGGLETAB TODO!"<<endl; | ||
1511 | break; | ||
1512 | default: //do nothing | ||
1513 | break; | ||
1514 | } | ||
1515 | |||
1516 | } | ||
1517 | 1151 | ||
1518 | /// handle system signals | 1152 | /// handle system signals |
1519 | void Fluxbox::handleSignal(int signum) { | 1153 | void Fluxbox::handleSignal(int signum) { |