Advertisement
Guest User

Untitled

a guest
Apr 19th, 2019
138
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 20.96 KB | None | 0 0
  1. import pickle
  2. import weakref
  3. from contextlib import contextmanager, suppress
  4. from datetime import datetime
  5. from itertools import islice
  6. from logging import getLogger
  7. from pathlib import Path
  8.  
  9. from libqtile import bar, hook, layout, widget
  10. from libqtile.command import lazy
  11. from libqtile.config import Click, Drag, Group, Key, Screen
  12. from libqtile.widget.base import ORIENTATION_HORIZONTAL
  13. from libqtile.widget.base import _TextBox as BaseTextBox
  14.  
  15. logger = getLogger('qtile.config')
  16.  
  17.  
  18. # TODO: multiple micro indicators will poll pulseaudio independently
  19. # TODO: microphone icon rendered
  20. # TODO: volume indicator
  21. # TODO: separate indicators for several microphones and outputs
  22. # TODO: background images for each group
  23. # TODO: backgrounds and proper drawing for bar
  24. # TODO: keyboard indicator/switcher
  25. # TODO: caps lock & group switching bug
  26. # TODO: screenshots without stealing focus
  27. # TODO: click on notification should reveat freshly made screenshot
  28. # TODO: automatically add additional groups ??
  29. # TODO: automatically clean clipboard after some time
  30. # TODO: schematic window layouts under group letters
  31.  
  32.  
  33. class PulseContext:
  34. VOLUME_STEP = 0.05
  35.  
  36. def __init__(self, pulse, ctl):
  37. self._pulse = weakref.ref(pulse)
  38. self._ctl = ctl
  39.  
  40. @property
  41. def pulse(self):
  42. return self._pulse()
  43.  
  44. def _change_volume(self, inc):
  45. for obj in self.pulse.sink_list():
  46. obj.volume.values = [min(1, max(0, v + inc)) for v in obj.volume.values]
  47. self.pulse.volume_set(obj, obj.volume)
  48.  
  49. def mute(self):
  50. for obj in self.pulse.sink_list():
  51. self.pulse.mute(obj, mute=not obj.mute)
  52.  
  53. def raise_volume(self):
  54. self._change_volume(self.VOLUME_STEP)
  55.  
  56. def lower_volume(self):
  57. self._change_volume(-self.VOLUME_STEP)
  58.  
  59. def get_micro(self):
  60. mute = True
  61. for obj in self.pulse.source_list():
  62. mute &= obj.mute
  63. return mute
  64.  
  65. def toggle_micro(self):
  66. mute = not self.get_micro()
  67. for obj in self.pulse.source_list():
  68. self.pulse.mute(obj, mute=mute)
  69. for cb in self._ctl._micro_callbacks:
  70. cb(mute)
  71. return mute
  72.  
  73.  
  74. class PulseControl:
  75. def __init__(self):
  76. self.Pulse = None
  77. try:
  78. from pulsectl import Pulse
  79. except ImportError:
  80. logger.error('You have to pip install pulsectl')
  81. else:
  82. self.Pulse = Pulse
  83. self._micro_callbacks = []
  84.  
  85. @property
  86. def valid(self):
  87. return self.Pulse is not None
  88.  
  89. @contextmanager
  90. def context(self):
  91. with self.Pulse('qtile-config') as pulse:
  92. yield PulseContext(pulse, self)
  93.  
  94. def make_cmd(self, func):
  95. def wrap(qtile):
  96. if self.valid:
  97. with self.context() as pctx:
  98. func(pctx)
  99. return lazy.function(wrap)
  100.  
  101. def register_micro_indicator(self, callback):
  102. self._micro_callbacks.append(callback)
  103. with self.context() as pctx:
  104. callback(pctx.get_micro())
  105.  
  106.  
  107. class Timer:
  108. def __init__(self):
  109. self.reset()
  110.  
  111. @property
  112. def current_gap(self):
  113. if self._current is None:
  114. return 0
  115. return (datetime.now() - self._current).total_seconds()
  116.  
  117. @property
  118. def active(self):
  119. return self._current is not None
  120.  
  121. @active.setter
  122. def active(self, value):
  123. if value == self.active:
  124. return
  125. if value:
  126. self._current = datetime.now()
  127. else:
  128. self._summed += self.current_gap
  129. self._current = None
  130.  
  131. def reset(self):
  132. self._summed = 0
  133. self._current = None
  134.  
  135. @property
  136. def seconds(self):
  137. return self._summed + self.current_gap
  138.  
  139. def __getstate__(self):
  140. # dict prevents value to be false
  141. return {'seconds': self.seconds}
  142.  
  143. def __setstate__(self, d):
  144. self._summed = d['seconds']
  145. self._current = None
  146.  
  147.  
  148. class MulticlickDetector:
  149. def __init__(self, clicks=3, time_period=2.0):
  150. assert clicks > 1
  151. assert time_period > 0
  152.  
  153. self.clicks = clicks
  154. self.time_period = time_period
  155.  
  156. self.counter = 0
  157. self.last = datetime.now()
  158.  
  159. def click(self):
  160. now = datetime.now()
  161. if (now - self.last).total_seconds() > self.time_period:
  162. self.counter = 0
  163. self.last = now
  164.  
  165. self.counter += 1
  166. if self.counter < self.clicks:
  167. return False
  168. self.counter = 0
  169. return True
  170.  
  171.  
  172. class PersistenceFilter:
  173. def __init__(self, timer: Timer):
  174. self.olddata = pickle.dumps(timer)
  175.  
  176. def update(self, timer: Timer):
  177. newdata = pickle.dumps(timer)
  178. if newdata == self.olddata:
  179. return False, None
  180. self.olddata = newdata
  181. return True, newdata
  182.  
  183.  
  184. class TimerPersistence:
  185. def __init__(self, file_path: Path):
  186. self.file_path = file_path
  187.  
  188. def load(self):
  189. timer = None
  190. with suppress(FileNotFoundError, EOFError, pickle.UnpicklingError):
  191. # TODO: check writable
  192. timer = pickle.loads(self.file_path.read_bytes())
  193. if timer is None:
  194. timer = Timer()
  195. self.pfilter = PersistenceFilter(timer)
  196. return timer
  197.  
  198. def flush(self, timer):
  199. should, bindata = self.pfilter.update(timer)
  200. if not should:
  201. return
  202. # TODO: check writable
  203. self.file_path.write_bytes(bindata)
  204.  
  205.  
  206. def format_timer(timer: Timer, spinner=True) -> str:
  207. spinner_chars = '⣾⣽⣻⢿⡿⣟⣯⣷'
  208.  
  209. def ft(s: int) -> str:
  210. too_small = s < 5 * 60
  211. result = []
  212. if s >= 3600:
  213. result.append(str(s // 3600) + 'h')
  214. s %= 3600
  215. if s >= 60:
  216. result.append(str(s // 60) + 'm')
  217. s %= 60
  218. if too_small:
  219. result.append(str(s) + 's')
  220. return ' '.join(result)
  221.  
  222. if not timer.active and timer.seconds <= 0:
  223. return '⌚'
  224. s = int(timer.seconds)
  225. result = ft(s)
  226. if spinner:
  227. result += spinner_chars[s % len(spinner_chars)]
  228. return result
  229.  
  230.  
  231. class CustomBaseTextBox(BaseTextBox):
  232. defaults = [
  233. ("text_shift", 0, "Shift text vertically"),
  234. ]
  235.  
  236. def __init__(self, text=" ", width=bar.CALCULATED, **config):
  237. super().__init__(text, width, **config)
  238. self.add_defaults(CustomBaseTextBox.defaults)
  239.  
  240. # exact copy of original code, with Y adjustment
  241. def draw(self):
  242. # if the bar hasn't placed us yet
  243. if self.offsetx is None:
  244. return
  245. self.drawer.clear(self.background or self.bar.background)
  246. self.layout.draw(
  247. self.actual_padding or 0,
  248. int(self.bar.height / 2.0 - self.layout.height / 2.0) + 1 + self.text_shift, # here
  249. )
  250. self.drawer.draw(offsetx=self.offsetx, width=self.width)
  251.  
  252.  
  253. class CustomCurrentLayout(CustomBaseTextBox):
  254. orientations = ORIENTATION_HORIZONTAL
  255.  
  256. def __init__(self, width=bar.CALCULATED, **config):
  257. super().__init__(text='', width=width, **config)
  258.  
  259. @staticmethod
  260. def get_layout_name(name):
  261. return ({
  262. 'max': '▣',
  263. 'columns': '▥',
  264. 'bsp': '◫',
  265. }).get(name, '[{}]'.format(name))
  266.  
  267. def _configure(self, qtile, bar):
  268. BaseTextBox._configure(self, qtile, bar)
  269. self.text = self.get_layout_name(self.bar.screen.group.layouts[0].name)
  270. self.setup_hooks()
  271.  
  272. def setup_hooks(self):
  273. def hook_response(layout, group):
  274. if group.screen is not None and group.screen == self.bar.screen:
  275. self.text = self.get_layout_name(layout.name)
  276. self.bar.draw()
  277. hook.subscribe.layout_change(hook_response)
  278.  
  279. def button_press(self, x, y, button):
  280. if button == 1:
  281. self.qtile.cmd_next_layout()
  282. elif button == 2:
  283. self.qtile.cmd_prev_layout()
  284.  
  285.  
  286. class MicroIndicator(CustomBaseTextBox):
  287. orientations = ORIENTATION_HORIZONTAL
  288. defaults = [
  289. ('active_color', 'ff0000', 'Color of active indicator'),
  290. ('inactive_color', '888888', 'Color of inactive indicator'),
  291. ]
  292.  
  293. def get_color(self, mute):
  294. return self.inactive_color if mute else self.active_color
  295.  
  296. def __init__(self, *, pulse_ctl, width=bar.CALCULATED, **config):
  297. super().__init__(text='⚫', width=width, **config)
  298. self.add_defaults(MicroIndicator.defaults)
  299. self.pulse_ctl = pulse_ctl
  300. self.pulse_ctl.register_micro_indicator(self.update_indicator)
  301.  
  302. def update_indicator(self, mute):
  303. new_color = self.get_color(mute)
  304. if self.foreground == new_color:
  305. return
  306. self.foreground = new_color
  307. if self.configured:
  308. # TODO: don't redraw whole bar
  309. self.bar.draw()
  310.  
  311. def cmd_toggle(self):
  312. "Toggle microphone."
  313. with self.pulse_ctl.context() as pctx:
  314. pctx.toggle_micro()
  315.  
  316. def button_press(self, x, y, button):
  317. if button == 1:
  318. self.cmd_toggle()
  319.  
  320. def timer_setup(self):
  321. self.timeout_add(3, self._auto_update)
  322.  
  323. def _auto_update(self):
  324. with self.pulse_ctl.context() as pctx:
  325. mute = pctx.get_micro()
  326. self.update_indicator(mute)
  327. self.timeout_add(2, self._auto_update)
  328.  
  329.  
  330. class TimeTracker(CustomBaseTextBox):
  331. orientations = ORIENTATION_HORIZONTAL
  332. defaults = [
  333. ('active_color', 'ff4000', 'Color of active indicator'),
  334. ('inactive_color', '888888', 'Color of inactive indicator'),
  335. ('update_interval', 1, 'Update interval in seconds'),
  336. ('save_interval', 300, 'Interval in seconds for persistence'),
  337. ]
  338.  
  339. def __init__(self, **config):
  340. super().__init__(text='', **config)
  341. self.add_defaults(TimeTracker.defaults)
  342. self.persist = TimerPersistence(Path.home() / '.tracker')
  343. self.timer = self.persist.load()
  344. self.resblocker = MulticlickDetector()
  345. self.formatter = format_timer
  346. self.update()
  347. if self.padding is None:
  348. self.padding = 4
  349.  
  350. def cmd_toggle(self):
  351. "Toggles timer (pause/unpause)."
  352. self.timer.active = not self.timer.active
  353. self.update()
  354.  
  355. def cmd_reset(self):
  356. "Resets timer."
  357. self.timer.reset()
  358. self.update()
  359.  
  360. def cmd_read(self, humanize=True):
  361. "Current amount of seconds."
  362. if humanize:
  363. return self.formatter(self.timer, spinner=False)
  364. return self.timer.seconds
  365.  
  366. def button_press(self, x, y, button):
  367. if button == 1:
  368. self.cmd_toggle()
  369. elif button == 3:
  370. if self.resblocker.click():
  371. self.cmd_reset()
  372.  
  373. def update(self):
  374. new_text = self.formatter(self.timer)
  375. new_color = self.active_color if self.timer.active else self.inactive_color
  376.  
  377. redraw = False
  378. redraw_bar = False
  379.  
  380. old_width = None
  381. if self.layout:
  382. old_width = self.layout.width
  383.  
  384. if new_color != self.foreground:
  385. redraw = True
  386. self.foreground = new_color
  387.  
  388. if new_text != self.text:
  389. redraw = True
  390. self.text = new_text
  391.  
  392. if not self.configured:
  393. return
  394.  
  395. if self.layout.width != old_width:
  396. redraw_bar = True
  397.  
  398. if redraw_bar:
  399. self.bar.draw()
  400. elif redraw:
  401. self.draw()
  402.  
  403. def timer_setup(self):
  404. self.timeout_add(self.update_interval, self._auto_update)
  405. self.timeout_add(self.save_interval, self._auto_persist)
  406.  
  407. def _auto_update(self):
  408. self.update()
  409. self.timeout_add(self.update_interval, self._auto_update)
  410.  
  411. def _auto_persist(self):
  412. self.persist.flush(self.timer)
  413. self.timeout_add(self.save_interval, self._auto_persist)
  414.  
  415.  
  416. hook.subscribe.hooks.add('prompt_focus')
  417. hook.subscribe.hooks.add('prompt_unfocus')
  418.  
  419.  
  420. class CustomPrompt(widget.Prompt):
  421. def startInput(self, *a, **kw): # noqa: N802
  422. hook.fire('prompt_focus')
  423. return super().startInput(*a, **kw)
  424.  
  425. def _unfocus(self):
  426. hook.fire('prompt_unfocus')
  427. return super()._unfocus()
  428.  
  429.  
  430. class CustomWindowName(widget.WindowName):
  431. def __init__(self, *a, **kw):
  432. super().__init__(*a, **kw)
  433. self.visible = True
  434.  
  435. def _configure(self, qtile, bar):
  436. super()._configure(qtile, bar)
  437. hook.subscribe._subscribe('prompt_focus', self.hide)
  438. hook.subscribe._subscribe('prompt_unfocus', self.show)
  439.  
  440. def show(self):
  441. self.visible = True
  442. self.update()
  443.  
  444. def hide(self):
  445. self.visible = False
  446. self.update()
  447.  
  448. def update(self, *args):
  449. if self.visible:
  450. super().update(*args)
  451. else:
  452. self.text = ''
  453. self.bar.draw()
  454.  
  455.  
  456. pulse_ctl = PulseControl()
  457.  
  458.  
  459. groups = []
  460. for gname in 'asdfghjkl':
  461. groups.append(Group(gname, label=gname.upper()))
  462.  
  463.  
  464. def user_keymap(mod, shift, control, alt):
  465. for g in groups:
  466. yield mod + g.name, lazy.group[g.name].toscreen()
  467. yield mod + shift + g.name, lazy.window.togroup(g.name)
  468.  
  469. yield mod + 'Down', lazy.layout.down()
  470. yield mod + 'Up', lazy.layout.up()
  471. yield mod + 'Left', lazy.layout.left()
  472. yield mod + 'Right', lazy.layout.right()
  473.  
  474. yield mod + shift + 'Down', lazy.layout.shuffle_down()
  475. yield mod + shift + 'Up', lazy.layout.shuffle_up()
  476. yield mod + shift + 'Left', lazy.layout.shuffle_left()
  477. yield mod + shift + 'Right', lazy.layout.shuffle_right()
  478.  
  479. yield mod + control + 'Down', lazy.layout.grow_down()
  480. yield mod + control + 'Up', lazy.layout.grow_up()
  481. yield mod + control + 'Left', lazy.layout.grow_left()
  482. yield mod + control + 'Right', lazy.layout.grow_right()
  483.  
  484. yield mod + alt + 'Down', lazy.layout.flip_down()
  485. yield mod + alt + 'Up', lazy.layout.flip_up()
  486. yield mod + alt + 'Left', lazy.layout.flip_left()
  487. yield mod + alt + 'Right', lazy.layout.flip_right()
  488.  
  489. yield mod + 'n', lazy.layout.normalize()
  490. yield mod + 'Tab', lazy.next_layout()
  491.  
  492. yield mod + 'F4', lazy.window.kill()
  493. yield mod + control + 'r', lazy.restart()
  494. yield mod + control + 'q', lazy.shutdown()
  495. yield mod + 'r', lazy.spawncmd()
  496.  
  497. yield mod + 'F10', lazy.window.toggle_floating()
  498. yield mod + 'F11', lazy.window.toggle_fullscreen()
  499.  
  500. yield 'XF86AudioMute', pulse_ctl.make_cmd(lambda pctx: pctx.mute())
  501. yield 'XF86AudioRaiseVolume', pulse_ctl.make_cmd(lambda pctx: pctx.raise_volume())
  502. yield 'XF86AudioLowerVolume', pulse_ctl.make_cmd(lambda pctx: pctx.lower_volume())
  503.  
  504. yield mod + 'Return', lazy.spawn('gnome-terminal')
  505. yield 'XF86HomePage', lazy.spawn('firefox')
  506. yield 'XF86Tools', lazy.spawn('audacious')
  507. yield 'XF86Mail', lazy.spawn('goldendict')
  508. yield 'XF86Explorer', lazy.spawn('nautilus')
  509.  
  510. shutter = 'shutter --exit_after_capture --no_session --disable_systray '
  511.  
  512. yield mod + 'Print', lazy.spawn(shutter + '--active')
  513. yield mod + control + 'Print', lazy.spawn(shutter + '--full')
  514.  
  515.  
  516. def make_keymap(user_map):
  517. result = []
  518.  
  519. class KeyCombo:
  520. def __init__(self, mods, key):
  521. self._mods = mods
  522. self._key = key
  523.  
  524. class KeyMods:
  525. def __init__(self, mods):
  526. self._mods = set(mods)
  527.  
  528. def __add__(self, other):
  529. if isinstance(other, KeyMods):
  530. return KeyMods(self._mods | other._mods)
  531. else:
  532. return KeyCombo(self._mods, other)
  533.  
  534. for k, cmd in user_map(
  535. KeyMods({'mod4'}),
  536. KeyMods({'shift'}),
  537. KeyMods({'control'}),
  538. KeyMods({'mod1'}),
  539. ):
  540. if isinstance(k, str):
  541. mods = set()
  542. elif isinstance(k, KeyCombo):
  543. mods = k._mods
  544. k = k._key
  545. else:
  546. logger.error('Bad key %s', k)
  547. continue
  548.  
  549. if 'lock' in mods:
  550. logger.error('You must not use "lock" modifier yourself')
  551. continue
  552.  
  553. result.append(Key(list(mods), k, cmd))
  554.  
  555. return result
  556.  
  557.  
  558. keys = make_keymap(user_keymap)
  559.  
  560.  
  561. class DarkWallpaperColorBox:
  562. text = '000000'
  563. inactive_text = '9b8976'
  564.  
  565. bg = 'e4ceb1'
  566. highlight_bg = 'd0aa78'
  567. urgent_bg = 'b81111'
  568.  
  569. border = '7a6e5e'
  570. border_focus = urgent_bg
  571. highlight_text = urgent_bg
  572.  
  573.  
  574. class GentooColorBox:
  575. bg = '54487A'
  576. highlight_bg = '6e56af'
  577. urgent_bg = '73d216'
  578.  
  579. text = 'ffffff'
  580. inactive_text = '776a9c'
  581.  
  582. border = '61538d'
  583. border_focus = urgent_bg
  584. highlight_text = urgent_bg
  585.  
  586.  
  587. class LightWallpaperColorBox:
  588. bg = '666666'
  589. highlight_bg = '888888'
  590. urgent_bg = 'fe8964'
  591.  
  592. text = 'ffffff'
  593. inactive_text = '333333'
  594.  
  595. border = '333333'
  596. border_focus = urgent_bg
  597. highlight_text = urgent_bg
  598.  
  599.  
  600. ColorBox = LightWallpaperColorBox
  601.  
  602.  
  603. class ScreenProxy:
  604. def __init__(self, real_screen, margin):
  605. self._s = real_screen
  606. self._margin = margin
  607.  
  608. @property
  609. def x(self):
  610. return self._s.x + self._margin
  611.  
  612. @property
  613. def y(self):
  614. return self._s.y + self._margin
  615.  
  616. @property
  617. def width(self):
  618. return self._s.width - self._margin * 2
  619.  
  620. @property
  621. def height(self):
  622. return self._s.height - self._margin * 2
  623.  
  624.  
  625. class FixedBsp(layout.Bsp):
  626. def configure(self, client, screen):
  627. amount = sum(1 for c in islice(self.root.clients(), 0, 2))
  628. super().configure(
  629. client,
  630. ScreenProxy(screen, self.margin if amount > 1 else -self.margin))
  631.  
  632.  
  633. layouts = [
  634. FixedBsp(
  635. border_width=3,
  636. border_normal=ColorBox.border,
  637. border_focus=ColorBox.border_focus,
  638. margin=5,
  639. name='bsp',
  640. ),
  641. layout.Max(),
  642. ]
  643. floating_layout = layout.Floating(
  644. border_width=3,
  645. border_normal=ColorBox.border,
  646. border_focus=ColorBox.border_focus,
  647. float_rules=[
  648. {'wmclass': 'confirm'},
  649. {'wmclass': 'dialog'},
  650. {'wmclass': 'download'},
  651. {'wmclass': 'error'},
  652. {'wmclass': 'file_progress'},
  653. {'wmclass': 'notification'},
  654. {'wmclass': 'splash'},
  655. {'wmclass': 'toolbar'},
  656. {'wmclass': 'confirmreset'}, # gitk
  657. {'wmclass': 'makebranch'}, # gitk
  658. {'wmclass': 'maketag'}, # gitk
  659. {'wname': 'branchdialog'}, # gitk
  660. {'wname': 'pinentry'}, # GPG key password entry
  661. {'wmclass': 'ssh-askpass'}, # ssh-askpass
  662. ],
  663. )
  664.  
  665.  
  666. widget_defaults = dict(
  667. font='PT Sans',
  668. fontsize=16,
  669. padding=0,
  670. padding_x=0,
  671. padding_y=0,
  672. margin=0,
  673. margin_x=0,
  674. margin_y=0,
  675. foreground=ColorBox.text,
  676. center_aligned=True,
  677. markup=False,
  678. )
  679.  
  680.  
  681. def create_widgets():
  682. yield widget.GroupBox(
  683. disable_drag=True,
  684. use_mouse_wheel=False,
  685. padding_x=4,
  686. padding_y=0,
  687. margin_y=4,
  688. spacing=0,
  689. borderwidth=0,
  690. highlight_method='block',
  691. urgent_alert_method='block',
  692. rounded=False,
  693. active=ColorBox.text,
  694. inactive=ColorBox.inactive_text,
  695. urgent_border=ColorBox.urgent_bg,
  696. this_current_screen_border=ColorBox.highlight_bg,
  697. fontsize=32,
  698. font='Old-Town',
  699. )
  700. yield CustomPrompt(
  701. padding=10,
  702. foreground=ColorBox.highlight_text,
  703. cursor_color=ColorBox.highlight_text,
  704. )
  705. yield CustomWindowName(padding=10)
  706. yield widget.Systray()
  707. yield TimeTracker(
  708. active_color=ColorBox.highlight_text,
  709. inactive_color=ColorBox.inactive_text,
  710. )
  711. yield widget.Clock(
  712. format='%e %a',
  713. foreground=ColorBox.inactive_text,
  714. font='PT Serif',
  715. update_interval=60,
  716. padding=2,
  717. )
  718. yield widget.Clock(
  719. format='%H:%M',
  720. foreground=ColorBox.text,
  721. fontsize=32,
  722. font='Old-Town',
  723. padding=2,
  724. )
  725. if pulse_ctl.valid:
  726. yield MicroIndicator(
  727. pulse_ctl=pulse_ctl,
  728. active_color=ColorBox.highlight_text,
  729. inactive_color=ColorBox.inactive_text,
  730. fontsize=24,
  731. text_shift=-1,
  732. )
  733. yield CustomCurrentLayout(
  734. padding=0,
  735. fontsize=26,
  736. foreground=ColorBox.inactive_text,
  737. text_shift=-3,
  738. )
  739.  
  740.  
  741. screens = [
  742. Screen(
  743. bottom=bar.Bar(
  744. list(create_widgets()),
  745. 22,
  746. background=ColorBox.bg,
  747. ),
  748. ),
  749. ]
  750.  
  751.  
  752. mouse = [
  753. Drag(['control'], 'Button9', lazy.window.set_position_floating(), start=lazy.window.get_position()),
  754. Drag(['mod4'], 'Button9', lazy.window.set_size_floating(), start=lazy.window.get_size()),
  755. Click([], 'Button9', lazy.widget['microindicator'].toggle(), focus=None),
  756. ]
  757.  
  758.  
  759. dgroups_key_binder = None
  760. dgroups_app_rules = []
  761. main = None
  762. follow_mouse_focus = False
  763. bring_front_click = True
  764. cursor_warp = False
  765. auto_fullscreen = True
  766. focus_on_window_activation = 'urgent'
  767.  
  768.  
  769. # XXX: Gasp! We're lying here. In fact, nobody really uses or cares about this
  770. # string besides java UI toolkits; you can see several discussions on the
  771. # mailing lists, github issues, and other WM documentation that suggest setting
  772. # this string if your java app doesn't work correctly. We may as well just lie
  773. # and say that we're a working one by default.
  774. #
  775. # We choose LG3D to maximize irony: it is a 3D non-reparenting WM written in
  776. # java that happens to be on java's whitelist.
  777. wmname = 'LG3D'
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement