Sway: Rewrite to replaceVars.

This commit is contained in:
Dmitry Voronin 2024-12-18 13:16:41 +03:00
parent ff6e01975f
commit d0af9eca9e
22 changed files with 424 additions and 532 deletions

View file

@ -1,4 +1,5 @@
{ {
__findFile,
config, config,
lib, lib,
pkgs, pkgs,
@ -6,7 +7,166 @@
... ...
}: }:
let let
swayRcRaw = pkgs.writeText "sway-rc-raw" (util.readFiles [ fontName = config.module.style.font.sansSerif.name;
keyboardLayouts = config.module.keyboard.layouts;
keyboardOptions = config.module.keyboard.options;
alpha = config.module.style.opacity.hex;
accent = config.module.style.color.accent + alpha;
border = config.module.style.color.border + alpha;
borderSize = config.module.style.window.border;
fg = config.module.style.color.fg.light;
wallpaper = config.module.wallpaper.path;
windowGap = config.module.style.window.gap;
codec = "libsvtav1";
color = config.module.style.color;
container = "mp4";
format = "%Y-%m-%d_%H-%M-%S";
framerate = 10;
opacity = "26";
pixfmt = "yuv420p10le";
selection = "slurp -d -b ${color.bg.light}${opacity} -c ${color.fg.light} -w 0 -s 00000000";
notifyStart = ''swayscript notify_short'';
notifyEnd = ''swayscript notify_long'';
picEdit = ''swappy -f - -o -'';
picFull = ''-o $(swaymsg -t get_outputs | jq -r ".[] | select(.focused) | .name") -'';
picPrepFile = prepFile "\${XDG_PICTURES_DIR[0]}" "png";
picRefLatestFile = refLatestFile "png";
picSelected = ''-g "''${scrSelection}" -'';
picToBuffer = ''wl-copy -t image/png'';
picToFile = ''tee "''${scrFile}"'';
screenshot = ''grim'';
updateWaybar = ''{ pkill -RTMIN+4 waybar; } & disown''; # NOTE: Might need to add a delay here if it becomes inconsistent one day.
vidFull = ''-o $(swaymsg -t get_outputs | jq -r ".[] | select(.focused) | .name") -'';
vidPrepFile = prepFile "\${XDG_VIDEOS_DIR[0]}" container;
vidRefLatestFile = refLatestFile container;
vidSelected = ''--geometry "''${scrSelection}"'';
vidStop = ''pkill -SIGINT wf-recorder'';
prepFile = path: ext: ''
# Focused app id by default.
curWindow=$(parse_snake $(swaymsg -t get_tree | jq '.. | select(.type?) | select(.focused==true) | .app_id'))
# If no id (i.e. xwayland), then use a name (title).
[[ "''${curWindow}" = "null" ]] && curWindow=$(parse_snake $(swaymsg -t get_tree | jq '.. | select(.type?) | select(.focused==true) | .name'))
# If no app in focus, use "unknown" dir.
[[ "''${curWindow}" =~ ^[0-9]+$ ]] && curWindow="unknown"
# Prepare dir and file path.
scrPath="${path}"
scrDir="${path}/''${curWindow}"
mkdir -p "''${scrDir}"
scrName="$(date +${format}).${ext}"
scrFile="''${scrDir}/''${scrName}"
scrLatestRef="./''${curWindow}/''${scrName}"
'';
refLatestFile = ext: ''
scrLatest="''${scrPath}/Latest.${ext}"
rm "''${scrLatest}"
ln -s "''${scrLatestRef}" "''${scrLatest}"
'';
getSelection = ''
scrSelection=$(${selection})
[[ -n "''${scrSelection}" ]] || exit
'';
getTransform = ''
scrTransform="$(swaymsg -t get_outputs | jq -r '.[] | select(.focused) | .transform')"
[[ "''${scrTransform}" = "normal" ]] && scrTransform=""
'';
vidStart = ''
wf-recorder \
--no-damage \
--codec ${codec} \
--file "''${scrFile}" \
--framerate ${toString framerate} \
--pixel-format ${pixfmt} \
'';
vidMuxAudio = ''
ffmpeg \
-f lavfi \
-i anullsrc=channel_layout=stereo:sample_rate=44100 \
-i "''${scrFile}" \
-c:v copy \
-c:a libopus \
-shortest \
-f ${container} \
"''${scrFile}_" \
&& mv "''${scrFile}_" "''${scrFile}" \
|| rm "''${scrFile}_"
'';
vidTransform = ''
if [[ -n "''${scrTransform}" ]]; then
ffmpeg \
-display_rotation ''${scrTransform} \
-i "''${scrFile}" \
-c copy \
-f ${container} \
"''${scrFile}_" \
&& mv "''${scrFile}_" "''${scrFile}" \
|| rm "''${scrFile}_"
fi
'';
SelectRecording = pkgs.writeShellScriptBin "SelectRecording" ''
${vidStop} || {
${getSelection}
${getTransform}
${vidPrepFile}
${notifyStart}
${updateWaybar}
${vidStart} ${vidSelected}
${notifyEnd}
${updateWaybar}
${vidMuxAudio}
${vidTransform}
${vidRefLatestFile}
};
'';
FullscreenRecording = pkgs.writeShellScriptBin "FullscreenRecording" ''
${vidStop} || {
${getTransform}
${vidPrepFile}
${notifyStart}
${updateWaybar}
${vidStart} ${vidFull}
${notifyEnd}
${updateWaybar}
${vidMuxAudio}
${vidTransform}
${vidRefLatestFile}
};
'';
FullscreenScreenshot = pkgs.writeShellScriptBin "FullscreenScreenshot" ''
${notifyEnd}
${picPrepFile}
${screenshot} ${picFull} | ${picToFile} | ${picToBuffer}
${picRefLatestFile}
'';
SelectScreenshot = pkgs.writeShellScriptBin "SelectScreenshot" ''
${getSelection}
${notifyStart}
${picPrepFile}
${screenshot} ${picSelected} | ${picEdit} | ${picToFile} | ${picToBuffer}
${notifyEnd}
${picRefLatestFile}
'';
swayRcRaw = pkgs.writeText "sway-rc-raw" (
util.readFiles [
./module/Mod.conf ./module/Mod.conf
./module/Style.conf ./module/Style.conf
./module/Display.conf ./module/Display.conf
@ -23,12 +183,32 @@ let
./module/Sound.conf ./module/Sound.conf
./module/Tiling.conf ./module/Tiling.conf
./module/Workspace.conf ./module/Workspace.conf
./module/Session.conf
./module/Keyd.conf ./module/Keyd.conf
./module/Waybar.conf ./module/Waybar.conf
./module/System.conf ./module/System.conf
./module/Mouse.conf ./module/Mouse.conf
]); ]
);
swayRc = (pkgs.replaceVars swayRcRaw {
inherit
FullscreenRecording
FullscreenScreenshot
SelectRecording
SelectScreenshot
accent
border
borderSize
fg
fontName
keyboardLayouts
keyboardOptions
wallpaper
windowGap
;
}).overrideAttrs (old: {
doCheck = false;
});
in in
{ {
text = text =
@ -36,6 +216,6 @@ in
# Read `man 5 sway` for a complete reference. # Read `man 5 sway` for a complete reference.
include /etc/sway/config.d/* include /etc/sway/config.d/*
'' ''
+ swayRc + builtins.readFile swayRc
+ lib.concatStringsSep "\n" config.module.sway.extraConfig; + lib.concatStringsSep "\n" config.module.sway.extraConfig;
} }

View file

@ -1,7 +1,2 @@
{ ... }:
{
text = ''
# You can get the names of your outputs by running: swaymsg -t get_outputs # You can get the names of your outputs by running: swaymsg -t get_outputs
output * scale 1 output * scale 1
'';
}

View file

@ -1,9 +1 @@
{ config, ... }: font "@fontName@ Medium 0.01"
let
fontName = config.module.style.font.sansSerif.name;
in
{
text = ''
font "${fontName} Medium 0.01"
'';
}

View file

@ -1,6 +1,3 @@
{ config, ... }:
{
text = ''
### Input configuration ### Input configuration
# #
# Example configuration: # Example configuration:
@ -34,8 +31,8 @@
} }
input type:keyboard { input type:keyboard {
xkb_layout ${config.module.keyboard.layouts} xkb_layout @keyboardLayouts@
xkb_options ${config.module.keyboard.options} xkb_options @keyboardOptions@
} }
# Hide mouse cursor after a period of inactivity. # Hide mouse cursor after a period of inactivity.
@ -43,5 +40,3 @@
# Per-window languages. # Per-window languages.
exec swaykbdd exec swaykbdd
'';
}

View file

@ -1,6 +1 @@
{ ... }:
{
text = ''
exec keyd-application-mapper -d exec keyd-application-mapper -d
'';
}

View file

@ -1,11 +1,6 @@
{ ... }:
{
text = ''
# Application launcher. # Application launcher.
# Note: pass the final command to swaymsg so that the resulting window can be opened # Note: pass the final command to swaymsg so that the resulting window can be opened
# on the original workspace that the command was run on. # on the original workspace that the command was run on.
set $menu fuzzel set $menu fuzzel
bindsym $mod+space exec $menu bindsym $mod+space exec $menu
'';
}

View file

@ -1,8 +1,3 @@
{ ... }:
{
text = ''
# Meta key. Use Mod1 for Alt. # Meta key. Use Mod1 for Alt.
set $mod Mod4 set $mod Mod4
set $sysmod print set $sysmod print
'';
}

View file

@ -1,8 +1,3 @@
{ ... }:
{
text = ''
bindsym --whole-window $mod+button2 kill bindsym --whole-window $mod+button2 kill
bindsym --whole-window $mod+button8 floating toggle bindsym --whole-window $mod+button8 floating toggle
bindsym --whole-window $mod+button9 fullscreen bindsym --whole-window $mod+button9 fullscreen
'';
}

View file

@ -1,6 +1,3 @@
{ ... }:
{
text = ''
# Move focus. # Move focus.
bindsym --to-code $mod+a focus left bindsym --to-code $mod+a focus left
bindsym --to-code $mod+d focus right bindsym --to-code $mod+d focus right
@ -15,5 +12,3 @@
# Focus mouse following. # Focus mouse following.
focus_follows_mouse yes focus_follows_mouse yes
'';
}

View file

@ -1,10 +1,5 @@
{ ... }:
{
text = ''
# Show last notification. # Show last notification.
bindsym --to-code $mod+shift+n exec makoctl restore bindsym --to-code $mod+shift+n exec makoctl restore
# Hide all notifications. # Hide all notifications.
bindsym --to-code $mod+n exec makoctl dismiss --all bindsym --to-code $mod+n exec makoctl dismiss --all
'';
}

View file

@ -1,13 +1,4 @@
{ ... }: bindsym --to-code $mod+j resize grow height 10px
let bindsym --to-code $mod+k resize shrink height 10px
stepHorizontal = 10; bindsym --to-code $mod+h resize shrink width 10px
stepVertical = 10; bindsym --to-code $mod+l resize grow width 10px
in
{
text = ''
bindsym --to-code $mod+j resize grow height ${toString stepVertical}px
bindsym --to-code $mod+k resize shrink height ${toString stepVertical}px
bindsym --to-code $mod+h resize shrink width ${toString stepHorizontal}px
bindsym --to-code $mod+l resize grow width ${toString stepHorizontal}px
'';
}

View file

@ -1,6 +1,3 @@
{ ... }:
{
text = ''
# Sway has a "scratchpad", which is a bag of holding for windows. # Sway has a "scratchpad", which is a bag of holding for windows.
# You can send windows there and get them back later. # You can send windows there and get them back later.
# NOTE: Get id with `swaymsg -t get_tree`. # NOTE: Get id with `swaymsg -t get_tree`.
@ -49,5 +46,3 @@
for_window [title="JamesDSP for Linux"] { for_window [title="JamesDSP for Linux"] {
move scratchpad move scratchpad
} }
'';
}

View file

@ -1,165 +1,5 @@
{ bindsym --to-code $mod+y exec @FullscreenScreenshot@/bin/FullscreenScreenshot}
__findFile, bindsym --to-code $mod+shift+y exec @FullscreenRecording@/bin/FullscreenRecording}
config,
lib,
pkgs,
...
}:
let
codec = "libsvtav1";
color = config.module.style.color;
container = "mp4";
format = "%Y-%m-%d_%H-%M-%S";
framerate = 10;
opacity = "26";
pixfmt = "yuv420p10le";
selection = "slurp -d -b ${color.bg.light}${opacity} -c ${color.fg.light} -w 0 -s 00000000";
in
{
text =
let
notifyStart = ''swayscript notify_short'';
notifyEnd = ''swayscript notify_long'';
picEdit = ''swappy -f - -o -'';
picFull = ''-o $(swaymsg -t get_outputs | jq -r ".[] | select(.focused) | .name") -'';
picPrepFile = prepFile "\${XDG_PICTURES_DIR[0]}" "png";
picRefLatestFile = refLatestFile "png";
picSelected = ''-g "''${scrSelection}" -'';
picToBuffer = ''wl-copy -t image/png'';
picToFile = ''tee "''${scrFile}"'';
screenshot = ''grim'';
updateWaybar = ''{ pkill -RTMIN+4 waybar; } & disown''; # NOTE: Might need to add a delay here if it becomes inconsistent one day.
vidFull = ''-o $(swaymsg -t get_outputs | jq -r ".[] | select(.focused) | .name") -'';
vidPrepFile = prepFile "\${XDG_VIDEOS_DIR[0]}" container;
vidRefLatestFile = refLatestFile container;
vidSelected = ''--geometry "''${scrSelection}"'';
vidStop = ''pkill -SIGINT wf-recorder'';
prepFile = path: ext: '' bindsym --to-code $mod+v exec @SelectScreenshot@/bin/SelectScreenshot}
# Focused app id by default. bindsym --to-code $mod+shift+v exec @SelectRecording@/bin/SelectRecording}
curWindow=$(parse_snake $(swaymsg -t get_tree | jq '.. | select(.type?) | select(.focused==true) | .app_id'))
# If no id (i.e. xwayland), then use a name (title).
[[ "''${curWindow}" = "null" ]] && curWindow=$(parse_snake $(swaymsg -t get_tree | jq '.. | select(.type?) | select(.focused==true) | .name'))
# If no app in focus, use "unknown" dir.
[[ "''${curWindow}" =~ ^[0-9]+$ ]] && curWindow="unknown"
# Prepare dir and file path.
scrPath="${path}"
scrDir="${path}/''${curWindow}"
mkdir -p "''${scrDir}"
scrName="$(date +${format}).${ext}"
scrFile="''${scrDir}/''${scrName}"
scrLatestRef="./''${curWindow}/''${scrName}"
'';
refLatestFile = ext: ''
scrLatest="''${scrPath}/Latest.${ext}"
rm "''${scrLatest}"
ln -s "''${scrLatestRef}" "''${scrLatest}"
'';
getSelection = ''
scrSelection=$(${selection})
[[ -n "''${scrSelection}" ]] || exit
'';
getTransform = ''
scrTransform="$(swaymsg -t get_outputs | jq -r '.[] | select(.focused) | .transform')"
[[ "''${scrTransform}" = "normal" ]] && scrTransform=""
'';
vidStart = ''
wf-recorder \
--no-damage \
--codec ${codec} \
--file "''${scrFile}" \
--framerate ${toString framerate} \
--pixel-format ${pixfmt} \
'';
vidMuxAudio = ''
ffmpeg \
-f lavfi \
-i anullsrc=channel_layout=stereo:sample_rate=44100 \
-i "''${scrFile}" \
-c:v copy \
-c:a libopus \
-shortest \
-f ${container} \
"''${scrFile}_" \
&& mv "''${scrFile}_" "''${scrFile}" \
|| rm "''${scrFile}_"
'';
vidTransform = ''
if [[ -n "''${scrTransform}" ]]; then
ffmpeg \
-display_rotation ''${scrTransform} \
-i "''${scrFile}" \
-c copy \
-f ${container} \
"''${scrFile}_" \
&& mv "''${scrFile}_" "''${scrFile}" \
|| rm "''${scrFile}_"
fi
'';
SelectRecording = pkgs.writeShellScriptBin "SelectRecording" ''
${vidStop} || {
${getSelection}
${getTransform}
${vidPrepFile}
${notifyStart}
${updateWaybar}
${vidStart} ${vidSelected}
${notifyEnd}
${updateWaybar}
${vidMuxAudio}
${vidTransform}
${vidRefLatestFile}
};
'';
FullscreenRecording = pkgs.writeShellScriptBin "FullscreenRecording" ''
${vidStop} || {
${getTransform}
${vidPrepFile}
${notifyStart}
${updateWaybar}
${vidStart} ${vidFull}
${notifyEnd}
${updateWaybar}
${vidMuxAudio}
${vidTransform}
${vidRefLatestFile}
};
'';
FullscreenScreenshot = pkgs.writeShellScriptBin "FullscreenScreenshot" ''
${notifyEnd}
${picPrepFile}
${screenshot} ${picFull} | ${picToFile} | ${picToBuffer}
${picRefLatestFile}
'';
SelectScreenshot = pkgs.writeShellScriptBin "SelectScreenshot" ''
${getSelection}
${notifyStart}
${picPrepFile}
${screenshot} ${picSelected} | ${picEdit} | ${picToFile} | ${picToBuffer}
${notifyEnd}
${picRefLatestFile}
'';
in
''
bindsym --to-code $mod+y exec ${lib.getExe FullscreenScreenshot}
bindsym --to-code $mod+shift+y exec ${lib.getExe FullscreenRecording}
bindsym --to-code $mod+v exec ${lib.getExe SelectScreenshot}
bindsym --to-code $mod+shift+v exec ${lib.getExe SelectRecording}
'';
}

View file

@ -1,10 +0,0 @@
{ config, ... }:
let
lock = "swaylock -f -F -c 000000 -k --font \"${config.module.style.font.serif.name}\" --font-size ${toString config.module.style.font.size.desktop}";
in
{
text = ''
bindsym --to-code $mod+z exec '_twice 1 ${lock}'
bindsym --to-code $mod+Shift+Z exec _twice 1 bash -c '${lock}; systemctl suspend -i'
'';
}

View file

@ -1,12 +1,5 @@
{ ... }: bindsym XF86AudioRaiseVolume exec 'pactl set-sink-volume @DEFAULT_SINK@ +5%'
let bindsym XF86AudioLowerVolume exec 'pactl set-sink-volume @DEFAULT_SINK@ -5%'
step_music = 10;
step_volume = 5;
in
{
text = ''
bindsym XF86AudioRaiseVolume exec 'pactl set-sink-volume @DEFAULT_SINK@ +${toString step_volume}%'
bindsym XF86AudioLowerVolume exec 'pactl set-sink-volume @DEFAULT_SINK@ -${toString step_volume}%'
bindsym XF86AudioMute exec 'pactl set-sink-mute @DEFAULT_SINK@ toggle' bindsym XF86AudioMute exec 'pactl set-sink-mute @DEFAULT_SINK@ toggle'
bindsym XF86AudioMicMute exec 'pactl set-source-mute @DEFAULT_SOURCE@ toggle' bindsym XF86AudioMicMute exec 'pactl set-source-mute @DEFAULT_SOURCE@ toggle'
@ -15,8 +8,6 @@ in
bindsym XF86AudioStop exec 'playerctl stop' bindsym XF86AudioStop exec 'playerctl stop'
bindsym XF86AudioNext exec 'playerctl next' bindsym XF86AudioNext exec 'playerctl next'
bindsym XF86AudioPrev exec 'playerctl previous' bindsym XF86AudioPrev exec 'playerctl previous'
bindsym XF86Forward exec 'playerctl position ${toString step_music}+' bindsym XF86Forward exec 'playerctl position 10+'
bindsym XF86Back exec 'playerctl position ${toString step_music}-' bindsym XF86Back exec 'playerctl position 10-'
bindsym XF86Eject exec 'swayscript sound_output_cycle' bindsym XF86Eject exec 'swayscript sound_output_cycle'
'';
}

View file

@ -1,17 +1,6 @@
{ config, ... }: output * bg @wallpaper@ fill
let client.focused "#@accent@" "#@accent@" "#@fg@" "#@accent@" "#@accent@"
accent = config.module.style.color.accent + alpha; client.focused_inactive "#@border@" "#@border@" "#@fg@" "#@border@" "#@border@"
alpha = config.module.style.opacity.hex; client.unfocused "#@border@" "#@border@" "#@fg@" "#@border@" "#@border@"
border = config.module.style.color.border + alpha; client.urgent "#@border@" "#@border@" "#@fg@" "#@border@" "#@border@"
fg = config.module.style.color.fg.light; client.placeholder "#@border@" "#@border@" "#@fg@" "#@border@" "#@border@"
in
{
text = ''
output * bg ${config.module.wallpaper.path} fill
client.focused "#${accent}" "#${accent}" "#${fg}" "#${accent}" "#${accent}"
client.focused_inactive "#${border}" "#${border}" "#${fg}" "#${border}" "#${border}"
client.unfocused "#${border}" "#${border}" "#${fg}" "#${border}" "#${border}"
client.urgent "#${border}" "#${border}" "#${fg}" "#${border}" "#${border}"
client.placeholder "#${border}" "#${border}" "#${fg}" "#${border}" "#${border}"
'';
}

View file

@ -1,9 +1,3 @@
{ ... }:
let
brstep = 5;
in
{
text = ''
bindsym $sysmod input * xkb_switch_layout 0 bindsym $sysmod input * xkb_switch_layout 0
bindsym --to-code $sysmod+a exec 'swayscript network' bindsym --to-code $sysmod+a exec 'swayscript network'
bindsym --to-code $sysmod+c exec 'systemctl reboot -i' bindsym --to-code $sysmod+c exec 'systemctl reboot -i'
@ -17,9 +11,7 @@ in
bindsym --to-code $sysmod+x exec 'systemctl poweroff -i' bindsym --to-code $sysmod+x exec 'systemctl poweroff -i'
bindsym --to-code $sysmod+z exec 'systemctl suspend -i' bindsym --to-code $sysmod+z exec 'systemctl suspend -i'
bindsym --to-code $sysmod+w exec light -A ${toString brstep} bindsym --to-code $sysmod+w exec light -A 5
bindsym XF86MonBrightnessUp exec light -A ${toString brstep} bindsym XF86MonBrightnessUp exec light -A 5
bindsym --to-code $sysmod+s exec light -U ${toString brstep} bindsym --to-code $sysmod+s exec light -U 5
bindsym XF86MonBrightnessDown exec light -U ${toString brstep} bindsym XF86MonBrightnessDown exec light -U 5
'';
}

View file

@ -1,8 +1,3 @@
{ ... }:
{
text = ''
set $term foot set $term foot
bindsym --to-code $mod+Escape exec $term -e bash -i -c "tmux new-session -A -s $USER; bash -i" bindsym --to-code $mod+Escape exec $term -e bash -i -c "tmux new-session -A -s $USER; bash -i"
'';
}

View file

@ -1,6 +1,3 @@
{ config, ... }:
{
text = ''
# Toggle tiling. # Toggle tiling.
bindsym --to-code $mod+shift+f floating toggle bindsym --to-code $mod+shift+f floating toggle
@ -28,9 +25,7 @@
bindsym --to-code $mod+x kill bindsym --to-code $mod+x kill
# Add gaps. # Add gaps.
gaps inner ${toString config.module.style.window.gap} gaps inner @windowGap@
# Launch everything tiled. # Launch everything tiled.
# for_window [all] floating disable # for_window [all] floating disable
'';
}

View file

@ -1,13 +1,5 @@
{ config, ... }:
let
borderSize = toString config.module.style.window.border;
in
{
text = ''
# Disable title bar. # Disable title bar.
default_border pixel ${borderSize} default_border pixel @borderSize@
default_floating_border pixel ${borderSize} default_floating_border pixel @borderSize@
titlebar_padding 1 titlebar_padding 1
titlebar_border_thickness 0 titlebar_border_thickness 0
'';
}

View file

@ -1,7 +1,2 @@
{ ... }:
{
text = ''
bindsym --to-code $mod+shift+r exec 'pkill waybar || exec waybar' bindsym --to-code $mod+shift+r exec 'pkill waybar || exec waybar'
exec waybar exec waybar
'';
}

View file

@ -1,6 +1,3 @@
{ ... }:
{
text = ''
# Switch to workspace. # Switch to workspace.
bindsym --to-code $mod+1 workspace number 1 bindsym --to-code $mod+1 workspace number 1
bindsym --to-code $mod+2 workspace number 2 bindsym --to-code $mod+2 workspace number 2
@ -34,5 +31,3 @@
# Move left-right. # Move left-right.
bindsym --to-code $mod+Shift+e move container to workspace next bindsym --to-code $mod+Shift+e move container to workspace next
bindsym --to-code $mod+Shift+q move container to workspace prev bindsym --to-code $mod+Shift+q move container to workspace prev
'';
}