File SwayNotificationCenter-0.12.3.obscpio of Package SwayNotificationCenter

07070100000000000081A4000000000000000000000001690B16EB00000061000000000000000000000000000000000000002C00000000SwayNotificationCenter-0.12.3/.clang-formatUseTab: Always
IndentWidth: 4
TabWidth: 4
ColumnLimit: 80
AllowShortFunctionsOnASingleLine: None
07070100000001000081A4000000000000000000000001690B16EB00000123000000000000000000000000000000000000002C00000000SwayNotificationCenter-0.12.3/.editorconfigroot = true

# elementary defaults
[*]
charset = utf-8
end_of_line = lf
indent_size = tab
indent_style = space
insert_final_newline = true
max_line_length = 100
tab_width = 4

# Markup files
[{*.html,*.xml,*.xml.in,*.yml}]
tab_width = 2

# Man pages
[*.scd]
indent_style = tab
tab_width = 4
07070100000002000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002600000000SwayNotificationCenter-0.12.3/.github07070100000003000081A4000000000000000000000001690B16EB000003A6000000000000000000000000000000000000003200000000SwayNotificationCenter-0.12.3/.github/FUNDING.yml# These are supported funding model platforms

github: [erikreider] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
thanks_dev: # Replace with a single thanks.dev username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
07070100000004000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000003500000000SwayNotificationCenter-0.12.3/.github/ISSUE_TEMPLATE07070100000005000081A4000000000000000000000001690B16EB000004A2000000000000000000000000000000000000004300000000SwayNotificationCenter-0.12.3/.github/ISSUE_TEMPLATE/bug_report.md---
name: Bug report
about: Create a report to help us improve
title: "[Bug]"
labels: bug
assignees: ''

---

**Please read through the README and the Man pages before submitting**
**Please also make sure that there isn't any prior issue describing this bug**

**If this is a CSS issue, please make sure that the `cssPriority` config option is set to "user" before reporting**

**Please also make sure that swaync is running with your primary GPU, and not PRIME**

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Please provide logs if you're experiencing notification errors / bugs**
Attach Gists for swaync logs by running `G_MESSAGES_DEBUG=all swaync` and (if applicable) application logs.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
 - OS: [e.g. Fedora 42]
 - Version [e.g. 0.12.0]

**Additional context**
Add any other context about the problem here.
07070100000006000081A4000000000000000000000001690B16EB000002F7000000000000000000000000000000000000004800000000SwayNotificationCenter-0.12.3/.github/ISSUE_TEMPLATE/feature_request.md---
name: Feature request
about: Suggest an idea for this project
title: "[Feature]"
labels: ''
assignees: ''

---

**Please read through the README and the Man pages before submitting**
**Please also make sure that there isn't any prior issue describing this feature**

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.
07070100000007000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000003000000000SwayNotificationCenter-0.12.3/.github/workflows07070100000008000081A4000000000000000000000001690B16EB0000060F000000000000000000000000000000000000004300000000SwayNotificationCenter-0.12.3/.github/workflows/PKGBUILD-build.yml# This is a basic workflow to help you get started with Actions

name: Check PKGBUILD builds for Arch.

# Controls when the workflow will run
on:
  # Triggers the workflow on push or pull request events but only for the main branch
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

jobs:
  PKGBUILD-build:
    container: archlinux:base-devel
    runs-on: ubuntu-latest
    steps:
      - name: Install packages
        run: |
          pacman-key --init
          pacman -Syu --noconfirm

      - name: Add builduser
        run: |
          # Create the builduser
          useradd builduser -m
          # Delete the buildusers password
          passwd -d builduser
          # Allow the builduser passwordless sudo
          printf 'builduser ALL=(ALL) ALL\n' | tee -a /etc/sudoers

      - name: Check out sources
        uses: actions/checkout@v3
        with:
          fetch-depth: 0
          path: swaync

      - name: Test PKGBUILD
        run: |
          cd swaync
          build_dir="/tmp/PKGBUILD-git"
          cd build-scripts

          mkdir $build_dir
          cp ./PKGBUILD-git $build_dir/PKGBUILD

          mkdir $build_dir/src
          cp -r ../ $build_dir/src/swaync

          cd $build_dir
          sudo chown -R builduser $build_dir
          # Use the locally checked-out branch instead of downloading the source
          sudo -H -u builduser bash -c "makepkg -s --noconfirm --noextract SRCDEST=$build_dir"
07070100000009000081A4000000000000000000000001690B16EB000005F4000000000000000000000000000000000000004100000000SwayNotificationCenter-0.12.3/.github/workflows/fedora-build.ymlname: Check build for Fedora.

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

jobs:
  fedora-build:
    container: registry.fedoraproject.org/fedora-minimal:latest
    runs-on: ubuntu-latest

    steps:
      - name: Install tooling for source RPM build
        run: |
          microdnf -y install --nodocs --setopt=install_weak_deps=0 \
            @development-tools @rpm-development-tools rpkg git \
            'dnf5-command(builddep)' 'dnf5-command(copr)'
          # Enable the official COPR repo
          # (in case there are dependencies missing in the upstream repos)
          microdnf copr enable erikreider/SwayNotificationCenter -y

      # It is necessary to checkout into sub-directory, because of some weird ownership problems cause by using containers
      - name: Check out sources
        uses: actions/checkout@v3
        with:
          fetch-depth: 0
          path: swaync

      - name: Copy spec into root dir
        run: |
          cd swaync
          cp ./build-scripts/swaync.rpkg.spec ./

      - name: Generate spec
        run: |
          cd swaync
          mkdir specs
          rpkg spec --source  --outdir specs

      - name: Install build dependencies
        run: |
          cd swaync
          microdnf -y builddep ./specs/swaync.rpkg.spec

      - name: Local build
        run: |
          cd swaync
          mkdir -p out
          rpkg local --out `pwd`/out
0707010000000A000081A4000000000000000000000001690B16EB00000672000000000000000000000000000000000000004000000000SwayNotificationCenter-0.12.3/.github/workflows/fedora-copr.ymlname: Package for Fedora Copr repo

on:
  release:
    types: [published]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

jobs:
  fedora-copr:
    container: registry.fedoraproject.org/fedora-minimal:latest
    runs-on: ubuntu-latest

    steps:
      - name: Install API token for copr-cli
        env:
          API_LOGIN: ${{ secrets.COPR_API_LOGIN }}
          API_USERNAME: ${{ secrets.COPR_API_USERNAME }}
          API_TOKEN_CONTENT: ${{ secrets.COPR_API_TOKEN }}
        run: |
          mkdir -p "$HOME/.config"
          # To generate a new token: https://copr.fedorainfracloud.org/api/.
          echo "[copr-cli]" >> "$HOME/.config/copr"
          echo "login = $API_LOGIN" >> "$HOME/.config/copr"
          echo "username = $API_USERNAME" >> "$HOME/.config/copr"
          echo "token = $API_TOKEN_CONTENT" >> "$HOME/.config/copr"
          echo "copr_url = https://copr.fedorainfracloud.org" >> "$HOME/.config/copr"

      - name: Install tooling for source RPM build
        run: |
          microdnf -y install --nodocs --setopt=install_weak_deps=0 \
            copr-cli rpkg git

      # It is necessary to checkout into sub-directory, because of some weird ownership problems cause by using containers
      - name: Check out sources
        uses: actions/checkout@v3
        with:
          fetch-depth: 0
          path: swaync

      - name: Copy spec into root dir
        run: |
          cd swaync
          cp ./build-scripts/swaync.rpkg.spec ./

      - name: Submit the build to copr
        run: |
          cd swaync
          rpkg -v copr-build -w ${{ secrets.COPR_REPO_NAME }}
0707010000000B000081A4000000000000000000000001690B16EB00000778000000000000000000000000000000000000003C00000000SwayNotificationCenter-0.12.3/.github/workflows/linting.yml# This is a basic workflow to help you get started with Actions

name: Linting

# Controls when the workflow will run
on:
  # Triggers the workflow on push or pull request events but only for the main branch
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

jobs:
  vala-linting:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - uses: elementary/actions/vala-lint@master
      with:
        dir: src/
        conf: .vala-lint.conf
        fail: true

  uncrustify:
    container: registry.fedoraproject.org/fedora-minimal:latest
    runs-on: ubuntu-latest
    steps:
      - name: Install uncrustify
        run: |
          microdnf -y install --nodocs --setopt=install_weak_deps=0 \
            @development-tools git \
            uncrustify

      - name: Check out sources
        uses: actions/checkout@v3
        with:
          fetch-depth: 0
          path: swaync

      - name: Run uncrustify
        run: |
          cd swaync
          uncrustify -c ./.uncrustify.cfg -l vala $(find . -name "*.vala" -type f) --check

  rpmlint:
    container: registry.fedoraproject.org/fedora-minimal:latest
    runs-on: ubuntu-latest
    steps:
      - name: Install rpmlint
        run: |
          microdnf -y install --nodocs --setopt=install_weak_deps=0 \
            rpmlint rpkg

      - name: Check out sources
        uses: actions/checkout@v3
        with:
          fetch-depth: 0
          path: swaync

      - name: Copy spec into root dir
        run: |
          cd swaync
          cp ./build-scripts/swaync.rpkg.spec ./

      - name: Generate spec
        run: |
          cd swaync
          mkdir -p specs
          rpkg spec --source --outdir specs

      - name: Start linting
        run: |
          cd swaync
          rpmlint ./specs/swaync.rpkg.spec
0707010000000C000081A4000000000000000000000001690B16EB00000412000000000000000000000000000000000000004100000000SwayNotificationCenter-0.12.3/.github/workflows/ubuntu-build.yml# This is a basic workflow to help you get started with Actions

name: Check build for latest Ubuntu version.

# Controls when the workflow will run
on:
  # Triggers the workflow on push or pull request events but only for the main branch
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

jobs:
  ubuntu-build:
    container: ubuntu:25.04
    runs-on: ubuntu-latest
    env:
      DEBIAN_FRONTEND: noninteractive
      PACKAGES: meson libwayland-dev libgtk-4-dev gobject-introspection libgirepository1.0-dev valac libjson-glib-dev libgtk4-layer-shell-dev scdoc libgee-0.8-dev libpulse-dev sassc libgranite-7-dev blueprint-compiler libadwaita-1-dev
    steps:
      - name: Install packages
        run: |
          apt update
          apt install -y $PACKAGES

      - name: Check out sources
        uses: actions/checkout@v3

      - name: Meson configure
        run: meson build

      - name: Build
        run: ninja -C build
0707010000000D000081A4000000000000000000000001690B16EB00000063000000000000000000000000000000000000002900000000SwayNotificationCenter-0.12.3/.gitignore*.ui~
*.ui#
pkg
build
SwayNotificationCenter
./swaync
./swaync*
./src/swaync
./src/swaync*
.cache/
0707010000000E000081A4000000000000000000000001690B16EB0001222F000000000000000000000000000000000000002E00000000SwayNotificationCenter-0.12.3/.uncrustify.cfg# Uncrustify 0.60
# Rules for vala
# Version: 0.5
# Refactored to match the style used on this project: https://github.com/GNOME/vala

#
# General options
#

# The type of line endings
newlines                                 = auto     # auto/lf/crlf/cr

# The original size of tabs in the input
input_tab_size                           = 8        # number

# The size of tabs in the output (only used if align_with_tabs=true)
output_tab_size                          = 4        # number

# The ASCII value of the string escape char, usually 92 (\) or 94 (^). (Pawn)
string_escape_char                       = 92       # number

# Alternate string escape char for Pawn. Only works right before the quote char.
string_escape_char2                      = 0        # number

# Replace tab characters found in string literals with the escape sequence \t
# instead.
string_replace_tab_chars                 = true    # true/false

# Allow interpreting '>=' and '>>=' as part of a template in 'void f(list<list<B>>=val);'.
# If true (default), 'assert(x<0 && y>=3)' will be broken.
# Improvements to template detection may make this option obsolete.
tok_split_gte                            = false    # false/true

# Control what to do with the UTF-8 BOM (recommend 'remove')
utf8_bom                                 = ignore   # ignore/add/remove/force

# If the file contains bytes with values between 128 and 255, but is not UTF-8, then output as UTF-8
utf8_byte                                = false    # false/true

# Force the output encoding to UTF-8
utf8_force                               = false    # false/true

#
# Indenting
#

# The number of columns to indent per level.
# Usually 2, 3, 4, or 8.
indent_columns                           = 4        # number

# The continuation indent. If non-zero, this overrides the indent of '(' and '=' continuation indents.
# For FreeBSD, this is set to 4. Negative value is absolute and not increased for each ( level
indent_continue                          = 0        # number

# How to use tabs when indenting code
# 0=spaces only
# 1=indent with tabs to brace level, align with spaces
# 2=indent and align with tabs, using spaces when not on a tabstop
indent_with_tabs                         = 0        # number

# Comments that are not a brace level are indented with tabs on a tabstop.
# Requires indent_with_tabs=2. If false, will use spaces.
indent_cmt_with_tabs                     = false    # false/true

# Whether to indent strings broken by '\' so that they line up
indent_align_string                      = false    # false/true

# The number of spaces to indent multi-line XML strings.
# Requires indent_align_string=True
indent_xml_string                        = 0        # number

# Spaces to indent '{' from level
indent_brace                             = 0        # number

# Whether braces are indented to the body level
indent_braces                            = false    # false/true

# Disabled indenting function braces if indent_braces is true
indent_braces_no_func                    = false    # false/true

# Disabled indenting class braces if indent_braces is true
indent_braces_no_class                   = false    # false/true

# Disabled indenting struct braces if indent_braces is true
indent_braces_no_struct                  = false    # false/true

# Indent based on the size of the brace parent, i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc.
indent_brace_parent                      = false    # false/true

# Whether the 'namespace' body is indented
indent_namespace                         = true    # false/true

# The number of spaces to indent a namespace block
indent_namespace_level                   = 0        # number

# If the body of the namespace is longer than this number, it won't be indented.
# Requires indent_namespace=true. Default=0 (no limit)
indent_namespace_limit                   = 0        # number

# Whether the 'extern "C"' body is indented
indent_extern                            = false    # false/true

# Whether the 'class' body is indented
indent_class                             = true    # false/true

# Whether to indent the stuff after a leading class colon
indent_class_colon                       = false    # false/true
# Whether to indent the stuff after a leading class initializer colon
indent_constr_colon                       = false    # false/true

# Virtual indent from the ':' for member initializers. Default is 2
indent_ctor_init_leading                 = 2        # number

# Additional indenting for constructor initializer list
indent_ctor_init                         = 0        # number

# False=treat 'else\nif' as 'else if' for indenting purposes
# True=indent the 'if' one level
indent_else_if                           = false    # false/true

# Amount to indent variable declarations after a open brace. neg=relative, pos=absolute
indent_var_def_blk                       = 0        # number

# Indent continued variable declarations instead of aligning.
indent_var_def_cont                      = false    # false/true

# True:  force indentation of function definition to start in column 1
# False: use the default behavior
indent_func_def_force_col1               = false    # false/true

# True:  indent continued function call parameters one indent level
# False: align parameters under the open paren
indent_func_call_param                   = false    # false/true

# Same as indent_func_call_param, but for function defs
indent_func_def_param                    = false    # false/true

# Same as indent_func_call_param, but for function protos
indent_func_proto_param                  = false    # false/true

# Same as indent_func_call_param, but for class declarations
indent_func_class_param                  = false    # false/true

# Same as indent_func_call_param, but for class variable constructors
indent_func_ctor_var_param               = false    # false/true

# Same as indent_func_call_param, but for templates
indent_template_param                    = false    # false/true

# Double the indent for indent_func_xxx_param options
indent_func_param_double                 = false    # false/true

# Indentation column for standalone 'const' function decl/proto qualifier
indent_func_const                        = 0        # number

# Indentation column for standalone 'throw' function decl/proto qualifier
indent_func_throw                        = 0        # number

# The number of spaces to indent a continued '->' or '.'
# Usually set to 0, 1, or indent_columns.
indent_member                            = 1        # number

# Spaces to indent single line ('//') comments on lines before code
indent_sing_line_comments                = 0        # number

# If set, will indent trailing single line ('//') comments relative
# to the code instead of trying to keep the same absolute column
indent_relative_single_line_comments     = false    # false/true

# Spaces to indent 'case' from 'switch'
# Usually 0 or indent_columns.
indent_switch_case                       = indent_columns        # number

indent_switch_body             = indent_columns

# Spaces to shift the 'case' line, without affecting any other lines
# Usually 0.
indent_case_shift                        = 0        # number

# Spaces to indent '{' from 'case'.
# By default, the brace will appear under the 'c' in case.
# Usually set to 0 or indent_columns.
indent_case_brace                        = 0        # number

# Whether to indent comments found in first column
indent_col1_comment                      = false    # false/true

# How to indent goto labels
#  >0 : absolute column where 1 is the leftmost column
#  <=0 : subtract from brace indent
indent_label                             = 1        # number

# Same as indent_label, but for access specifiers that are followed by a colon
indent_access_spec                       = 1        # number

# Indent the code after an access specifier by one level.
# If set, this option forces 'indent_access_spec=0'
indent_access_spec_body                  = false    # false/true

# If an open paren is followed by a newline, indent the next line so that it lines up after the open paren (not recommended)
indent_paren_nl                          = false    # false/true

# Controls the indent of a close paren after a newline.
# 0: Indent to body level
# 1: Align under the open paren
# 2: Indent to the brace level
indent_paren_close                       = 2        # number

# Controls the indent of a comma when inside a paren.If TRUE, aligns under the open paren
indent_comma_paren                       = 0    # false/true

# Controls the indent of a BOOL operator when inside a paren.If TRUE, aligns under the open paren
indent_bool_paren                        = 0    # false/true

# If 'indent_bool_paren' is true, controls the indent of the first expression. If TRUE, aligns the first expression to the following ones
indent_first_bool_expr                   = false    # false/true

# If an open square is followed by a newline, indent the next line so that it lines up after the open square (not recommended)
indent_square_nl                         = false    # false/true

# Don't change the relative indent of ESQL/C 'EXEC SQL' bodies
indent_preserve_sql                      = false    # false/true

# Align continued statements at the '='. Default=True
# If FALSE or the '=' is followed by a newline, the next line is indent one tab.
indent_align_assign                      = false     # false/true

# Indent OC blocks at brace level instead of usual rules.
indent_oc_block                          = false    # false/true

# Indent OC blocks in a message relative to the parameter name.
# 0=use indent_oc_block rules, 1+=spaces to indent
indent_oc_block_msg                      = 0        # number

# Minimum indent for subsequent parameters
indent_oc_msg_colon                      = 0        # number

# Objective C

# If true, prioritize aligning with initial colon (and stripping spaces from lines, if necessary).
# Default is true.
indent_oc_msg_prioritize_first_colon    = true

# If indent_oc_block_msg and this option are on, blocks will be indented the way that Xcode does by default (from keyword if the parameter is on its own line; otherwise, from the previous indentation level).
indent_oc_block_msg_xcode_style         = true

# If indent_oc_block_msg and this option are on, blocks will be indented from where the brace is relative to a msg keyword.
indent_oc_block_msg_from_keyword        = true

# If indent_oc_block_msg and this option are on, blocks will be indented from where the brace is relative to a msg colon.
indent_oc_block_msg_from_colon          = true

# If indent_oc_block_msg and this option are on, blocks will be indented from where the block caret is.
indent_oc_block_msg_from_caret          = true

# If indent_oc_block_msg and this option are on, blocks will be indented from where the brace is.
indent_oc_block_msg_from_brace          = true

#
# Spacing options
#

# Add or remove space around arithmetic operator '+', '-', '/', '*', etc
sp_arith                                 = force   # ignore/add/remove/force

# Add or remove space around assignment operator '=', '+=', etc
sp_assign                                = force   # ignore/add/remove/force

# Add or remove space around '=' in C++11 lambda capture specifications. Overrides sp_assign
sp_cpp_lambda_assign                     = force   # ignore/add/remove/force

# Add or remove space after the capture specification in C++11 lambda.
# sp_cpp_lambda_paren                      = force   # ignore/add/remove/force

# Add or remove space around assignment operator '=' in a prototype
sp_assign_default                        = force   # ignore/add/remove/force

# Add or remove space before assignment operator '=', '+=', etc. Overrides sp_assign.
sp_before_assign                         = force   # ignore/add/remove/force

# Add or remove space after assignment operator '=', '+=', etc. Overrides sp_assign.
sp_after_assign                          = force   # ignore/add/remove/force

# Add or remove space around assignment '=' in enum
sp_enum_assign                           = force   # ignore/add/remove/force

# Add or remove space before assignment '=' in enum. Overrides sp_enum_assign.
sp_enum_before_assign                    = ignore   # ignore/add/remove/force

# Add or remove space after assignment '=' in enum. Overrides sp_enum_assign.
sp_enum_after_assign                     = force   # ignore/add/remove/force

# Add or remove space around preprocessor '##' concatenation operator. Default=Add
sp_pp_concat                             = add      # ignore/add/remove/force

# Add or remove space after preprocessor '#' stringify operator. Also affects the '#@' charizing operator.
sp_pp_stringify                          = ignore   # ignore/add/remove/force

# Add or remove space before preprocessor '#' stringify operator as in '#define x(y) L#y'.
sp_before_pp_stringify                   = ignore   # ignore/add/remove/force

# Add or remove space around boolean operators '&&' and '||'
sp_bool                                  = force   # ignore/add/remove/force

# Add or remove space around compare operator '<', '>', '==', etc
sp_compare                               = force   # ignore/add/remove/force

# Add or remove space inside '(' and ')'
sp_inside_paren                          = remove   # ignore/add/remove/force

# Add or remove space between nested parens
sp_paren_paren                           = remove   # ignore/add/remove/force

# Add or remove space between back-to-back parens: ')(' vs ') ('
sp_cparen_oparen                          = remove   # ignore/add/remove/force
# Whether to balance spaces inside nested parens
sp_balance_nested_parens                 = false    # false/true

# Add or remove space between ')' and '{'
sp_paren_brace                           = force   # ignore/add/remove/force

# Add or remove space before pointer star '*'
sp_before_ptr_star                       = force   # ignore/add/remove/force

# Add or remove space before pointer star '*' that isn't followed by a variable name
# If set to 'ignore', sp_before_ptr_star is used instead.
sp_before_unnamed_ptr_star               = force   # ignore/add/remove/force

# Add or remove space between pointer stars '*'
sp_between_ptr_star                      = remove   # ignore/add/remove/force

# Add or remove space after pointer star '*', if followed by a word.
sp_after_ptr_star                        = remove   # ignore/add/remove/force

# Add or remove space after a pointer star '*', if followed by a func proto/def.
sp_after_ptr_star_func                   = remove   # ignore/add/remove/force

# Add or remove space after a pointer star '*', if followed by an open paren (function types).
sp_ptr_star_paren                        = remove   # ignore/add/remove/force

# Add or remove space before a pointer star '*', if followed by a func proto/def.
sp_before_ptr_star_func                  = force   # ignore/add/remove/force

# Add or remove space before a reference sign '&'
sp_before_byref                          = force   # ignore/add/remove/force

# Add or remove space before a reference sign '&' that isn't followed by a variable name
# If set to 'ignore', sp_before_byref is used instead.
sp_before_unnamed_byref                  = ignore   # ignore/add/remove/force

# Add or remove space after reference sign '&', if followed by a word.
sp_after_byref                           = ignore   # ignore/add/remove/force

# Add or remove space after a reference sign '&', if followed by a func proto/def.
sp_after_byref_func                      = remove   # ignore/add/remove/force

# Add or remove space before a reference sign '&', if followed by a func proto/def.
sp_before_byref_func                     = force   # ignore/add/remove/force

# Add or remove space between type and word. Default=Force
sp_after_type                            = force    # ignore/add/remove/force

# Add or remove space before the paren in the D constructs 'template Foo(' and 'class Foo('.
sp_before_template_paren                 = force   # ignore/add/remove/force

# Add or remove space in 'template <' vs 'template<'.
# If set to ignore, sp_before_angle is used.
sp_template_angle                        = ignore   # ignore/add/remove/force

# Add or remove space before '<>'
sp_before_angle                          = remove   # ignore/add/remove/force

# Add or remove space inside '<' and '>'
sp_inside_angle                          = remove   # ignore/add/remove/force

# Add or remove space after '<>'
sp_after_angle                           = remove   # ignore/add/remove/force

# Add or remove space between '<>' and '(' as found in 'new List<byte>();'
sp_angle_paren                           = force   # ignore/add/remove/force

# Add or remove space between '<>' and a word as in 'List<byte> m;'
sp_angle_word                            = force   # ignore/add/remove/force

# Add or remove space between '>' and '>' in '>>' (template stuff C++/C# only). Default=Add
sp_angle_shift                           = add      # ignore/add/remove/force

# Permit removal of the space between '>>' in 'foo<bar<int> >' (C++11 only). Default=False
# sp_angle_shift cannot remove the space without this option.
sp_permit_cpp11_shift                    = false    # false/true

# Add or remove space before '(' of 'if', 'for', 'switch', and 'while'
sp_before_sparen                         = force   # ignore/add/remove/force

# Add or remove space inside if-condition '(' and ')'
sp_inside_sparen                         = remove   # ignore/add/remove/force

# Add or remove space before if-condition ')'. Overrides sp_inside_sparen.
sp_inside_sparen_close                   = ignore   # ignore/add/remove/force

# Add or remove space before if-condition '('. Overrides sp_inside_sparen.
sp_inside_sparen_open                    = ignore   # ignore/add/remove/force

# Add or remove space after ')' of 'if', 'for', 'switch', and 'while'
sp_after_sparen                          = force   # ignore/add/remove/force

# Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while'
sp_sparen_brace                          = force   # ignore/add/remove/force

# Add or remove space between 'invariant' and '(' in the D language.
sp_invariant_paren                       = ignore   # ignore/add/remove/force

# Add or remove space after the ')' in 'invariant (C) c' in the D language.
sp_after_invariant_paren                 = ignore   # ignore/add/remove/force

# Add or remove space before empty statement ';' on 'if', 'for' and 'while'
sp_special_semi                          = remove   # ignore/add/remove/force

# Add or remove space before ';'. Default=Remove
sp_before_semi                           = remove   # ignore/add/remove/force

# Add or remove space before ';' in non-empty 'for' statements
sp_before_semi_for                       = remove   # ignore/add/remove/force

# Add or remove space before a semicolon of an empty part of a for statement.
sp_before_semi_for_empty                 = force   # ignore/add/remove/force

# Add or remove space after ';', except when followed by a comment. Default=Add
sp_after_semi                            = add      # ignore/add/remove/force

# Add or remove space after ';' in non-empty 'for' statements. Default=Force
sp_after_semi_for                        = force    # ignore/add/remove/force

# Add or remove space after the final semicolon of an empty part of a for statement: for ( ; ; <here> ).
sp_after_semi_for_empty                  = force   # ignore/add/remove/force

# Add or remove space before '[' (except '[]')
sp_before_square                         = remove   # ignore/add/remove/force

# Add or remove space before '[]'
sp_before_squares                        = remove   # ignore/add/remove/force

# Add or remove space inside a non-empty '[' and ']'
sp_inside_square                         = remove   # ignore/add/remove/force

# Add or remove space after ','
sp_after_comma                           = force   # ignore/add/remove/force

# Add or remove space before ','
sp_before_comma                          = remove   # ignore/add/remove/force

# Add or remove space between an open paren and comma: '(,' vs '( ,'
sp_paren_comma                           = force    # ignore/add/remove/force

# Add or remove space before the variadic '...' when preceded by a non-punctuator
sp_before_ellipsis                       = remove   # ignore/add/remove/force

# Add or remove space after class ':'
sp_after_class_colon                     = force   # ignore/add/remove/force

# Add or remove space before class ':'
sp_before_class_colon                    = force   # ignore/add/remove/force

# Add or remove space after class constructor ':'
sp_after_constr_colon                     = ignore   # ignore/add/remove/force

# Add or remove space before class constructor ':'
sp_before_constr_colon                    = ignore   # ignore/add/remove/force

# Add or remove space before case ':'. Default=Remove
sp_before_case_colon                     = remove   # ignore/add/remove/force

# Add or remove space between 'operator' and operator sign
sp_after_operator                        = force   # ignore/add/remove/force

# Add or remove space between the operator symbol and the open paren, as in 'operator ++('
sp_after_operator_sym                    = ignore   # ignore/add/remove/force

# Add or remove space after C/D cast, i.e. 'cast(int)a' vs 'cast(int) a' or '(int)a' vs '(int) a'
sp_after_cast                            = force   # ignore/add/remove/force

# Add or remove spaces inside cast parens
sp_inside_paren_cast                     = remove   # ignore/add/remove/force

# Add or remove space between the type and open paren in a C++ cast, i.e. 'int(exp)' vs 'int (exp)'
sp_cpp_cast_paren                        = ignore   # ignore/add/remove/force

# Add or remove space between 'sizeof' and '('
sp_sizeof_paren                          = force   # ignore/add/remove/force

# Add or remove space after the tag keyword (Pawn)
sp_after_tag                             = ignore   # ignore/add/remove/force

# Add or remove space inside enum '{' and '}'
sp_inside_braces_enum                    = force   # ignore/add/remove/force

# Add or remove space inside struct/union '{' force '}'
sp_inside_braces_struct                  = force   # ignore/add/remove/force

# Add or remove space inside '{' and '}'
sp_inside_braces                         = force   # ignore/add/remove/force

# Add or remove space inside '{}'
sp_inside_braces_empty                   = remove   # ignore/add/remove/force

# Add or remove space between return type and function name
# A minimum of 1 is forced except for pointer return types.
sp_type_func                             = remove   # ignore/add/remove/force

# Add or remove space between function name and '(' on function declaration
sp_func_proto_paren                      = force   # ignore/add/remove/force

# CARL duplicates ERROR ??
# Add or remove space between function name and '(' on function definition
sp_func_def_paren                        = force   # ignore/add/remove/force

# Add or remove space inside empty function '()'
sp_inside_fparens                        = remove   # ignore/add/remove/force

# Add or remove space inside function '(' and ')'
sp_inside_fparen                         = remove   # ignore/add/remove/force

# Add or remove space inside the first parens in the function type: 'void (*x)(...)'
sp_inside_tparen                         = remove   # ignore/add/remove/force

# Add or remove between the parens in the function type: 'void (*x)(...)'
sp_after_tparen_close                    = remove   # ignore/add/remove/force

# Add or remove space between ']' and '(' when part of a function call.
sp_square_fparen                         = force   # ignore/add/remove/force

# Add or remove space between ')' and '{' of function
sp_fparen_brace                          = force   # ignore/add/remove/force

# Add or remove space between function name and '(' on function calls
sp_func_call_paren                       = force   # ignore/add/remove/force

# Add or remove space between function name and '()' on function calls without parameters.
# If set to 'ignore' (the default), sp_func_call_paren is used.
sp_func_call_paren_empty                 = force   # ignore/add/remove/force

# Add or remove space between the user function name and '(' on function calls
# You need to set a keyword to be a user function, like this: 'set func_call_user _' in the config file.
sp_func_call_user_paren                  = ignore   # ignore/add/remove/force
set func_call_user _

# Add or remove space between a constructor/destructor and the open paren
sp_func_class_paren                      = force   # ignore/add/remove/force

# Add or remove space between 'return' and '('
sp_return_paren                          = force   # ignore/add/remove/force

# Add or remove space between '__attribute__' and '('
sp_attribute_paren                       = force   # ignore/add/remove/force

# Add or remove space between 'defined' and '(' in '#if defined (FOO)'
sp_defined_paren                         = force   # ignore/add/remove/force

# Add or remove space between 'throw' and '(' in 'throw (something)'
sp_throw_paren                           = force   # ignore/add/remove/force

# Add or remove space between 'throw' and anything other than '(' as in '@throw [...];'
sp_after_throw                           = force   # ignore/add/remove/force

# Add or remove space between 'catch' and '(' in 'catch (something) { }'
# If set to ignore, sp_before_sparen is used.
sp_catch_paren                           = force   # ignore/add/remove/force

# D
# Add or remove space between 'version' and '(' in 'version (something) { }' (D language)
# If set to ignore, sp_before_sparen is used.
sp_version_paren                         = ignore   # ignore/add/remove/force

# D
# Add or remove space between 'scope' and '(' in 'scope (something) { }' (D language)
# If set to ignore, sp_before_sparen is used.
sp_scope_paren                           = ignore   # ignore/add/remove/force

# Add or remove space between macro and value
sp_macro                                 = ignore   # ignore/add/remove/force

# MACRO
# Add or remove space between macro function ')' and value
sp_macro_func                            = ignore   # ignore/add/remove/force

# Add or remove space between 'else' and '{' if on the same line
sp_else_brace                            = force   # ignore/add/remove/force

# Add or remove space between '}' and 'else' if on the same line
sp_brace_else                            = force   # ignore/add/remove/force

# Add or remove space between '}' and the name of a typedef on the same line
sp_brace_typedef                         = force   # ignore/add/remove/force

# Add or remove space between 'catch' and '{' if on the same line
sp_catch_brace                           = force   # ignore/add/remove/force

# Add or remove space between '}' and 'catch' if on the same line
sp_brace_catch                           = force   # ignore/add/remove/force

# Add or remove space between 'finally' and '{' if on the same line
sp_finally_brace                         = force   # ignore/add/remove/force

# Add or remove space between '}' and 'finally' if on the same line
sp_brace_finally                         = force   # ignore/add/remove/force

# Add or remove space between 'try' and '{' if on the same line
sp_try_brace                             = force   # ignore/add/remove/force

# Add or remove space between get/set and '{' if on the same line
sp_getset_brace                          = force   # ignore/add/remove/force

# CARL TODO

# Add or remove space between a variable and '{' for C++ uniform initialization
# sp_word_brace                             = ignore

# Add or remove space between a variable and '{' for a namespace
sp_word_brace_ns                          = force

# C++
# Add or remove space before the '::' operator
sp_before_dc                             = remove   # ignore/add/remove/force

# C++
# Add or remove space after the '::' operator
sp_after_dc                              = remove   # ignore/add/remove/force

# Add or remove around the D named array initializer ':' operator
sp_d_array_colon                         = ignore   # ignore/add/remove/force

# Add or remove space after the '!' (not) operator. Default=Remove
sp_not                                   = remove   # ignore/add/remove/force

# Add or remove space after the '~' (invert) operator. Default=Remove
sp_inv                                   = remove   # ignore/add/remove/force

# Add or remove space after the '&' (address-of) operator. Default=Remove
# This does not affect the spacing after a '&' that is part of a type.
sp_addr                                  = remove   # ignore/add/remove/force

# Add or remove space around the '.' or '->' operators. Default=Remove
sp_member                                = remove   # ignore/add/remove/force

# Add or remove space after the '*' (dereference) operator. Default=Remove
# This does not affect the spacing after a '*' that is part of a type.
sp_deref                                 = remove   # ignore/add/remove/force

# Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. Default=Remove
sp_sign                                  = remove   # ignore/add/remove/force

# Add or remove space before or after '++' and '--', as in '(--x)' or 'y++;'. Default=Remove
sp_incdec                                = remove   # ignore/add/remove/force

# Add or remove space before a backslash-newline at the end of a line. Default=Add
sp_before_nl_cont                        = add      # ignore/add/remove/force

# Obj c
# Add or remove space after the scope '+' or '-', as in '-(void) foo;' or '+(int) bar;'
sp_after_oc_scope                        = ignore   # ignore/add/remove/force

# Obj c
# Add or remove space after the colon in message specs
# '-(int) f:(int) x;' vs '-(int) f: (int) x;'
sp_after_oc_colon                        = ignore   # ignore/add/remove/force

# Obj c
# Add or remove space before the colon in message specs
# '-(int) f: (int) x;' vs '-(int) f : (int) x;'
sp_before_oc_colon                       = ignore   # ignore/add/remove/force

# Obj c
# Add or remove space after the colon in immutable dictionary expression
# 'NSDictionary *test = @{@"foo" :@"bar"};'
sp_after_oc_dict_colon                   = ignore   # ignore/add/remove/force

# Obj c
# Add or remove space before the colon in immutable dictionary expression
# 'NSDictionary *test = @{@"foo" :@"bar"};'
sp_before_oc_dict_colon                  = ignore   # ignore/add/remove/force

# Obj c
# Add or remove space after the colon in message specs
# '[object setValue:1];' vs '[object setValue: 1];'
sp_after_send_oc_colon                   = ignore   # ignore/add/remove/force

# Obj c
# Add or remove space before the colon in message specs
# '[object setValue:1];' vs '[object setValue :1];'
sp_before_send_oc_colon                  = ignore   # ignore/add/remove/force

# Obj c
# Add or remove space after the (type) in message specs
# '-(int)f: (int) x;' vs '-(int)f: (int)x;'
sp_after_oc_type                         = ignore   # ignore/add/remove/force

# Obj c
# Add or remove space after the first (type) in message specs
# '-(int) f:(int)x;' vs '-(int)f:(int)x;'
sp_after_oc_return_type                  = ignore   # ignore/add/remove/force

# Obj c
# Add or remove space between '@selector' and '('
# '@selector(msgName)' vs '@selector (msgName)'
# Also applies to @protocol() constructs
sp_after_oc_at_sel                       = ignore   # ignore/add/remove/force

# Obj c
# Add or remove space between '@selector(x)' and the following word
# '@selector(foo) a:' vs '@selector(foo)a:'
sp_after_oc_at_sel_parens                = ignore   # ignore/add/remove/force

# Obj c
# Add or remove space inside '@selector' parens
# '@selector(foo)' vs '@selector( foo )'
# Also applies to @protocol() constructs
sp_inside_oc_at_sel_parens               = ignore   # ignore/add/remove/force

# Obj c
# Add or remove space before a block pointer caret
# '^int (int arg){...}' vs. ' ^int (int arg){...}'
sp_before_oc_block_caret                 = ignore   # ignore/add/remove/force

# Obj c
# Add or remove space after a block pointer caret
# '^int (int arg){...}' vs. '^ int (int arg){...}'
sp_after_oc_block_caret                  = ignore   # ignore/add/remove/force

# Obj c
# Add or remove space between the receiver and selector in a message.
# '[receiver selector ...]'
sp_after_oc_msg_receiver                 = ignore   # ignore/add/remove/force

# Obj c
# Add or remove space after @property.
sp_after_oc_property                     = ignore   # ignore/add/remove/force

# Add or remove space around the ':' in 'b ? t : f'
sp_cond_colon                            = force   # ignore/add/remove/force
# TODO

# Add or remove space before the ':' in 'b ? t : f'. Overrides sp_cond_colon.
sp_cond_colon_before                      = force
#  Add or remove space after the ':' in 'b ? t : f'. Overrides sp_cond_colon.
sp_cond_colon_after                       = force
# Add or remove space around the '?' in 'b ? t : f'
sp_cond_question                          = force

# Add or remove space before the '?' in 'b ? t : f'. Overrides sp_cond_question.
sp_cond_question_before                   = force

# Add or remove space after the '?' in 'b ? t : f'. Overrides sp_cond_question.
sp_cond_question_after                    = force

# In the abbreviated ternary form (a ?: b), add/remove space between ? and :.'. Overrides all other sp_cond_* options.
sp_cond_ternary_short                     = force

# Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make sense here.
sp_case_label                            = force   # ignore/add/remove/force

# Control the space around the D '..' operator.
sp_range                                 = ignore   # ignore/add/remove/force

# Control the spacing after ':' in 'for (TYPE VAR : EXPR)' (Java)
sp_after_for_colon                        = ignore   # ignore/add/remove/force

# Control the spacing before ':' in 'for (TYPE VAR : EXPR)' (Java)
sp_before_for_colon                       = ignore   # ignore/add/remove/force

# Control the spacing in 'extern (C)' (D)
sp_extern_paren                          = ignore   # ignore/add/remove/force

# Control the space after the opening of a C++ comment '// A' vs '//A'
sp_cmt_cpp_start                         = force    # ignore/add/remove/force

# Controls the spaces between #else or #endif and a trailing comment
sp_endif_cmt                             = remove   # ignore/add/remove/force

# Controls the spaces after 'new', 'delete', and 'delete[]'
sp_after_new                             = force   # ignore/add/remove/force

# Controls the spaces before a trailing or embedded comment
sp_before_tr_emb_cmt                     = force   # ignore/add/remove/force

# Number of spaces before a trailing or embedded comment
sp_num_before_tr_emb_cmt                 = 0        # number

# Control space between a Java annotation and the open paren.
sp_annotation_paren                      = ignore   # ignore/add/remove/force

#
# Code alignment (not left column spaces/tabs)
#

# Whether to keep non-indenting tabs
align_keep_tabs                          = false    # false/true

# Whether to use tabs for aligning
align_with_tabs                          = false    # false/true

# Whether to bump out to the next tab when aligning
align_on_tabstop                         = false    # false/true

# Whether to left-align numbers
# align_number_left                        = false    # false/true

# TODO DOC
# Whether to keep whitespace not required for alignment.
align_keep_extra_space                    = true

# Align variable definitions in prototypes and functions
align_func_params                        = false    # false/true

# Align parameters in single-line functions that have the same name.
# The function names must already be aligned with each other.
align_same_func_call_params              = false    # false/true

# The span for aligning variable definitions (0=don't align)
align_var_def_span                       = 0        # number

# How to align the star in variable definitions.
#  0=Part of the type     'void *   foo;'
#  1=Part of the variable 'void     *foo;'
#  2=Dangling             'void    *foo;'
align_var_def_star_style                 = 1        # number

# How to align the '&' in variable definitions.
#  0=Part of the type
#  1=Part of the variable
#  2=Dangling
align_var_def_amp_style                  = 1        # number

# The threshold for aligning variable definitions (0=no limit)
align_var_def_thresh                     = 0        # number

# The gap for aligning variable definitions
align_var_def_gap                        = 0        # number

# Whether to align the colon in struct bit fields
align_var_def_colon                      = false    # false/true

# Whether to align any attribute after the variable name
align_var_def_attribute                  = false    # false/true

# Whether to align inline struct/enum/union variable definitions
align_var_def_inline                     = false    # false/true

# The span for aligning on '=' in assignments (0=don't align)
align_assign_span                        = 0        # number

# The threshold for aligning on '=' in assignments (0=no limit)
align_assign_thresh                      = 0        # number

# The span for aligning on '=' in enums (0=don't align)
align_enum_equ_span                      = 0        # number

# The threshold for aligning on '=' in enums (0=no limit)
align_enum_equ_thresh                    = 0        # number

# The span for aligning struct/union (0=don't align)
align_var_struct_span                    = 0        # number

# The threshold for aligning struct/union member definitions (0=no limit)
align_var_struct_thresh                  = 0        # number

# The gap for aligning struct/union member definitions
align_var_struct_gap                     = 0        # number

# The span for aligning struct initializer values (0=don't align)
align_struct_init_span                   = 0        # number

# The minimum space between the type and the synonym of a typedef
align_typedef_gap                        = 0        # number

# The span for aligning single-line typedefs (0=don't align)
align_typedef_span                       = 0        # number

# How to align typedef'd functions with other typedefs
# 0: Don't mix them at all
# 1: align the open paren with the types
# 2: align the function type name with the other type names
align_typedef_func                       = 0        # number

# Controls the positioning of the '*' in typedefs. Just try it.
# 0: Align on typedef type, ignore '*'
# 1: The '*' is part of type name: typedef int  *pint;
# 2: The '*' is part of the type, but dangling: typedef int *pint;
align_typedef_star_style                 = 0        # number

# Controls the positioning of the '&' in typedefs. Just try it.
# 0: Align on typedef type, ignore '&'
# 1: The '&' is part of type name: typedef int  &pint;
# 2: The '&' is part of the type, but dangling: typedef int &pint;
align_typedef_amp_style                  = 0        # number

# The span for aligning comments that end lines (0=don't align)
align_right_cmt_span                     = 0        # number

# If aligning comments, mix with comments after '}' and #endif with less than 3 spaces before the comment
align_right_cmt_mix                      = false    # false/true

# If a trailing comment is more than this number of columns away from the text it follows,
# it will qualify for being aligned. This has to be > 0 to do anything.
align_right_cmt_gap                      = 0        # number

# Align trailing comment at or beyond column N; 'pulls in' comments as a bonus side effect (0=ignore)
align_right_cmt_at_col                   = 0        # number

# The span for aligning function prototypes (0=don't align)
align_func_proto_span                    = 0        # number

# Minimum gap between the return type and the function name.
align_func_proto_gap                     = 0        # number

# Align function protos on the 'operator' keyword instead of what follows
align_on_operator                        = false    # false/true

# Whether to mix aligning prototype and variable declarations.
# If true, align_var_def_XXX options are used instead of align_func_proto_XXX options.
align_mix_var_proto                      = false    # false/true

# Align single-line functions with function prototypes, uses align_func_proto_span
align_single_line_func                   = false    # false/true

# Aligning the open brace of single-line functions.
# Requires align_single_line_func=true, uses align_func_proto_span
align_single_line_brace                  = false    # false/true

# Gap for align_single_line_brace.
align_single_line_brace_gap              = 0        # number

# The span for aligning ObjC msg spec (0=don't align)
align_oc_msg_spec_span                   = 0        # number

# Whether to align macros wrapped with a backslash and a newline.
# This will not work right if the macro contains a multi-line comment.
align_nl_cont                            = false    # false/true

# # Align macro functions and variables together
align_pp_define_together                 = false    # false/true

# The minimum space between label and value of a preprocessor define
align_pp_define_gap                      = 0        # number

# The span for aligning on '#define' bodies (0=don't align)
align_pp_define_span                     = 0        # number

# Align lines that start with '<<' with previous '<<'. Default=true
align_left_shift                         = true     # false/true

# Span for aligning parameters in an Obj-C message call on the ':' (0=don't align)
align_oc_msg_colon_span                  = 0        # number

# If true, always align with the first parameter, even if it is too short.
align_oc_msg_colon_first                 = false    # false/true

# Aligning parameters in an Obj-C '+' or '-' declaration on the ':'
align_oc_decl_colon                      = false    # false/true

#
# Newline adding and removing options
#

# Whether to collapse empty blocks between '{' and '}'
nl_collapse_empty_body                   = true    # false/true

# Don't split one-line braced assignments - 'foo_t f = { 1, 2 };'
nl_assign_leave_one_liners               = true    # false/true

# Don't split one-line braced statements inside a class xx { } body
nl_class_leave_one_liners                = false    # false/true

# Don't split one-line enums: 'enum foo { BAR = 15 };'
nl_enum_leave_one_liners                 = false    # false/true

# Don't split one-line get or set functions
nl_getset_leave_one_liners               = false    # false/true

# Don't split one-line function definitions - 'int foo() { return 0; }'
nl_func_leave_one_liners                 = false    # false/true

# Don't split one-line if/else statements - 'if(a) b++;'
nl_if_leave_one_liners                   = false    # false/true

# Don't split one-line OC messages
nl_oc_msg_leave_one_liner                = false    # false/true

# Add or remove newlines at the start of the file
nl_start_of_file                         = ignore   # ignore/add/remove/force

# The number of newlines at the start of the file (only used if nl_start_of_file is 'add' or 'force'
nl_start_of_file_min                     = 0        # number

# Add or remove newline at the end of the file
nl_end_of_file                           = add   # ignore/add/remove/force

# The number of newlines at the end of the file (only used if nl_end_of_file is 'add' or 'force')
nl_end_of_file_min                       = 1        # number

# Add or remove newline between '=' and '{'
nl_assign_brace                          = ignore   # ignore/add/remove/force

# Add or remove newline between '=' and '[' (D only)
nl_assign_square                         = ignore   # ignore/add/remove/force

# Add or remove newline after '= [' (D only). Will also affect the newline before the ']'
nl_after_square_assign                   = ignore   # ignore/add/remove/force

# The number of blank lines after a block of variable definitions at the top of a function body
# 0 = No change (default)
nl_func_var_def_blk                      = 0        # number

# The number of newlines before a block of typedefs
# 0 = No change (default)
nl_typedef_blk_start                     = 0        # number

# The number of newlines after a block of typedefs
# 0 = No change (default)
nl_typedef_blk_end                       = 0        # number

# The maximum consecutive newlines within a block of typedefs
# 0 = No change (default)
nl_typedef_blk_in                        = 0        # number

# The number of newlines before a block of variable definitions not at the top of a function body
# 0 = No change (default)
nl_var_def_blk_start                     = 0        # number

# The number of newlines after a block of variable definitions not at the top of a function body
# 0 = No change (default)
nl_var_def_blk_end                       = 0        # number

# The maximum consecutive newlines within a block of variable definitions
# 0 = No change (default)
nl_var_def_blk_in                        = 0        # number

# Add or remove newline between a function call's ')' and '{', as in:
# list_for_each(item, &list) { }
nl_fcall_brace                           = ignore   # ignore/add/remove/force

# Add or remove newline between 'enum' and '{'
nl_enum_brace                            = remove   # ignore/add/remove/force

# Add or remove newline between 'struct and '{'
nl_struct_brace                          = remove   # ignore/add/remove/force

# Add or remove newline between 'union' and '{'
nl_union_brace                           = remove   # ignore/add/remove/force

# Add or remove newline between 'if' and '{'
nl_if_brace                              = remove   # ignore/add/remove/force

# Add or remove newline between '}' and 'else'
nl_brace_else                            = remove   # ignore/add/remove/force

# Add or remove newline between 'else if' and '{'
# If set to ignore, nl_if_brace is used instead
nl_elseif_brace                          = remove   # ignore/add/remove/force

# Add or remove newline between 'else' and '{'
nl_else_brace                            = remove   # ignore/add/remove/force

# Add or remove newline between 'else' and 'if'
nl_else_if                               = remove   # ignore/add/remove/force

# Add or remove newline between '}' and 'finally'
nl_brace_finally                         = remove   # ignore/add/remove/force

# Add or remove newline between 'finally' and '{'
nl_finally_brace                         = remove   # ignore/add/remove/force

# Add or remove newline between 'try' and '{'
nl_try_brace                             = remove   # ignore/add/remove/force

# Add or remove newline between get/set and '{'
nl_getset_brace                          = remove   # ignore/add/remove/force

# Add or remove newline between 'for' and '{'
nl_for_brace                             = remove   # ignore/add/remove/force

# Add or remove newline between 'catch' and '{'
nl_catch_brace                           = remove   # ignore/add/remove/force

# Add or remove newline between '}' and 'catch'
nl_brace_catch                           = remove   # ignore/add/remove/force

# Add or remove newline between '}' and ']'
nl_brace_square                           = remove   # ignore/add/remove/force

# Add or remove newline between '}' and ')' in a function invocation
nl_brace_fparen                           = remove   # ignore/add/remove/force
# Add or remove newline between 'while' and '{'
nl_while_brace                           = remove   # ignore/add/remove/force

# Add or remove newline between 'scope (x)' and '{' (D)
nl_scope_brace                           = ignore   # ignore/add/remove/force

# Add or remove newline between 'unittest' and '{' (D)
nl_unittest_brace                        = ignore   # ignore/add/remove/force

# Add or remove newline between 'version (x)' and '{' (D)
nl_version_brace                         = ignore   # ignore/add/remove/force

# Add or remove newline between 'using' and '{'
nl_using_brace                           = remove   # ignore/add/remove/force

# Add or remove newline between two open or close braces.
# Due to general newline/brace handling, REMOVE may not work.
nl_brace_brace                           = ignore   # ignore/add/remove/force

# Add or remove newline between 'do' and '{'
nl_do_brace                              = remove   # ignore/add/remove/force

# Add or remove newline between '}' and 'while' of 'do' statement
nl_brace_while                           = remove   # ignore/add/remove/force

# Add or remove newline between 'switch' and '{'
nl_switch_brace                          = remove   # ignore/add/remove/force

# Add a newline between ')' and '{' if the ')' is on a different line than the if/for/etc.
# Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch, and nl_catch_brace.
nl_multi_line_cond                       = false    # false/true

# Force a newline in a define after the macro name for multi-line defines.
nl_multi_line_define                     = false    # false/true

# Whether to put a newline before 'case' statement
nl_before_case                           = false    # false/true

# Add or remove newline between ')' and 'throw'
nl_before_throw                          = remove   # ignore/add/remove/force

# Whether to put a newline after 'case' statement
nl_after_case                            = false    # false/true

# Add or remove a newline between a case ':' and '{'. Overrides nl_after_case.
nl_case_colon_brace                      = remove   # ignore/add/remove/force

# Newline between namespace and {
nl_namespace_brace                       = remove   # ignore/add/remove/force

# Add or remove newline between 'template<>' and whatever follows.
nl_template_class                        = ignore   # ignore/add/remove/force

# Add or remove newline between 'class' and '{'
nl_class_brace                           = remove   # ignore/add/remove/force

# Add or remove newline after each ',' in the class base list
nl_class_init_args                        = remove   # ignore/add/remove/force
# Add or remove newline after each ',' in the constructor member initialization
nl_class_init_args                       = remove   # ignore/add/remove/force

# Add or remove newline between return type and function name in a function definition
nl_func_type_name                        = remove   # ignore/add/remove/force

# Add or remove newline between return type and function name inside a class {}
# Uses nl_func_type_name or nl_func_proto_type_name if set to ignore.
nl_func_type_name_class                  = remove   # ignore/add/remove/force

# Add or remove newline between function scope and name in a definition
# Controls the newline after '::' in 'void A::f() { }'
nl_func_scope_name                       = ignore   # ignore/add/remove/force

# Add or remove newline between return type and function name in a prototype
nl_func_proto_type_name                  = remove   # ignore/add/remove/force

# Add or remove newline between a function name and the opening '('
nl_func_paren                            = remove   # ignore/add/remove/force

# Add or remove newline between a function name and the opening '(' in the definition
nl_func_def_paren                        = remove   # ignore/add/remove/force

# Add or remove newline after '(' in a function declaration
nl_func_decl_start                       = remove   # ignore/add/remove/force

# Add or remove newline after '(' in a function definition
nl_func_def_start                        = remove   # ignore/add/remove/force

# Overrides nl_func_decl_start when there is only one parameter.
nl_func_decl_start_single                = ignore   # ignore/add/remove/force

# Overrides nl_func_def_start when there is only one parameter.
nl_func_def_start_single                 = ignore   # ignore/add/remove/force

# Add or remove newline after each ',' in a function declaration
nl_func_decl_args                        = ignore   # ignore/add/remove/force

# Add or remove newline after each ',' in a function definition
nl_func_def_args                         = ignore   # ignore/add/remove/force

# Add or remove newline before the ')' in a function declaration
nl_func_decl_end                         = remove   # ignore/add/remove/force

# Add or remove newline before the ')' in a function definition
nl_func_def_end                          = remove   # ignore/add/remove/force

# Overrides nl_func_decl_end when there is only one parameter.
nl_func_decl_end_single                  = ignore   # ignore/add/remove/force

# Overrides nl_func_def_end when there is only one parameter.
nl_func_def_end_single                   = ignore   # ignore/add/remove/force

# Add or remove newline between '()' in a function declaration.
nl_func_decl_empty                       = remove   # ignore/add/remove/force

# Add or remove newline between '()' in a function definition.
nl_func_def_empty                        = remove   # ignore/add/remove/force

# Whether to put each OC message parameter on a separate line
# See nl_oc_msg_leave_one_liner
nl_oc_msg_args                           = false    # false/true

# Add or remove newline between function signature and '{'
nl_fdef_brace                            = remove   # ignore/add/remove/force

# Add or remove newline between C++11 lambda signature and '{'
nl_cpp_ldef_brace                         = ignore   # ignore/add/remove/force

# Add or remove a newline between the return keyword and return expression.
nl_return_expr                           = remove   # ignore/add/remove/force

# Whether to put a newline after semicolons, except in 'for' statements
nl_after_semicolon                       = false    # false/true

# CARL ??
# Whether to put a newline after brace open.
# This also adds a newline before the matching brace close.
nl_after_brace_open                      = false    # false/true

# If nl_after_brace_open and nl_after_brace_open_cmt are true, a newline is
# placed between the open brace and a trailing single-line comment.
nl_after_brace_open_cmt                  = false    # false/true

# Whether to put a newline after a virtual brace open with a non-empty body.
# These occur in un-braced if/while/do/for statement bodies.
nl_after_vbrace_open                     = false    # false/true

# Whether to put a newline after a virtual brace open with an empty body.
# These occur in un-braced if/while/do/for statement bodies.
nl_after_vbrace_open_empty               = false    # false/true

# Whether to put a newline after a brace close.
# Does not apply if followed by a necessary ';'.
nl_after_brace_close                     = false    # false/true

# Whether to put a newline after a virtual brace close.
# Would add a newline before return in: 'if (foo) a++; return;'
nl_after_vbrace_close                    = false    # false/true

# Control the newline between the close brace and 'b' in: 'struct { int a; } b;'
# Affects enums, unions, and structures. If set to ignore, uses nl_after_brace_close
nl_brace_struct_var                      = ignore   # ignore/add/remove/force

# Whether to alter newlines in '#define' macros
nl_define_macro                          = false    # false/true

# Whether to not put blanks after '#ifxx', '#elxx', or before '#endif'
nl_squeeze_ifdef                         = false    # false/true

# Add or remove blank line before 'if'
nl_before_if                             = ignore   # ignore/add/remove/force

# Add or remove blank line after 'if' statement
nl_after_if                              = ignore   # ignore/add/remove/force

# Add or remove blank line before 'for'
nl_before_for                            = ignore   # ignore/add/remove/force

# Add or remove blank line after 'for' statement
nl_after_for                             = ignore   # ignore/add/remove/force

# Add or remove blank line before 'while'
nl_before_while                          = ignore   # ignore/add/remove/force

# Add or remove blank line after 'while' statement
nl_after_while                           = ignore   # ignore/add/remove/force

# Add or remove blank line before 'switch'
nl_before_switch                         = ignore   # ignore/add/remove/force

# Add or remove blank line after 'switch' statement
nl_after_switch                          = ignore   # ignore/add/remove/force

# Add or remove blank line before 'do'
nl_before_do                             = ignore   # ignore/add/remove/force

# Add or remove blank line after 'do/while' statement
nl_after_do                              = ignore   # ignore/add/remove/force

# Whether to double-space commented-entries in struct/enum
nl_ds_struct_enum_cmt                    = false    # false/true

# Whether to double-space before the close brace of a struct/union/enum
# (lower priority than 'eat_blanks_before_close_brace')
nl_ds_struct_enum_close_brace            = false    # false/true

# Add or remove a newline around a class colon.
# Related to pos_class_colon, nl_class_init_args, and pos_comma.
nl_class_colon                           = ignore   # ignore/add/remove/force

# Add or remove a newline around a class constructor colon.
# Related to pos_constr_colon, nl_constr_init_args, and pos_constr_comma.
nl_constr_colon                           = ignore   # ignore/add/remove/force


# Change simple unbraced if statements into a one-liner
# 'if(b)\n i++;' => 'if(b) i++;'
nl_create_if_one_liner                   = false    # false/true

# Change simple unbraced for statements into a one-liner
# 'for (i=0;i<5;i++)\n foo(i);' => 'for (i=0;i<5;i++) foo(i);'
nl_create_for_one_liner                  = false    # false/true

# Change simple unbraced while statements into a one-liner
# 'while (i<5)\n foo(i++);' => 'while (i<5) foo(i++);'
nl_create_while_one_liner                = false    # false/true

#
# Positioning options
#

# The position of arithmetic operators in wrapped expressions
pos_arith                                = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force

# The position of assignment in wrapped expressions.
# Do not affect '=' followed by '{'
pos_assign                               = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force

# The position of boolean operators in wrapped expressions
pos_bool                                 = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force

# The position of comparison operators in wrapped expressions
pos_compare                              = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force

# The position of conditional (b ? t : f) operators in wrapped expressions
pos_conditional                          = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force

# The position of the comma in wrapped expressions
pos_comma                                = trail   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force

# The position of the comma in the class base list
pos_class_comma                           = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force

# The position of the comma in the constructor initialization list
pos_constr_comma                          = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force

# The position of colons between class and base class list
pos_class_colon                           = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force

# The position of colons between constructor and member initialization
pos_constr_colon                          = ignore   # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force

#
# Line Splitting options
#

# Try to limit code width to N number of columns
code_width                               = 100        # number

# Whether to fully split long 'for' statements at semi-colons
ls_for_split_full                        = false    # false/true

# Whether to fully split long function protos/calls at commas
ls_func_split_full                       = false    # false/true

# Whether to split lines as close to code_width as possible and ignore some groupings
ls_code_width                            = false    # false/true

#
# Blank line options
#

# The maximum consecutive newlines
nl_max                                   = 0        # number

# The number of newlines after a function prototype, if followed by another function prototype
nl_after_func_proto                      = 0        # number

# The number of newlines after a function prototype, if not followed by another function prototype
nl_after_func_proto_group                = 2        # number

# The number of newlines after '}' of a multi-line function body
nl_after_func_body                       = 2        # number

# The number of newlines after '}' of a multi-line function body in a class declaration
nl_after_func_body_class                 = 2        # number

# The number of newlines after '}' of a single line function body
nl_after_func_body_one_liner             = 0        # number

# The minimum number of newlines before a multi-line comment.
# Doesn't apply if after a brace open or another multi-line comment.
nl_before_block_comment                  = 0        # number

# The minimum number of newlines before a single-line C comment.
# Doesn't apply if after a brace open or other single-line C comments.
nl_before_c_comment                      = 0        # number

# The minimum number of newlines before a CPP comment.
# Doesn't apply if after a brace open or other CPP comments.
nl_before_cpp_comment                    = 0        # number

# Whether to force a newline after a multi-line comment.
nl_after_multiline_comment               = false    # false/true

# The number of newlines after '}' or ';' of a struct/enum/union definition
nl_after_struct                          = 2        # number

# The number of newlines after '}' or ';' of a class definition
nl_after_class                           = 2        # number

# The number of newlines before a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label.
# Will not change the newline count if after a brace open.
# 0 = No change.
nl_before_access_spec                    = 0        # number

# The number of newlines after a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label.
# 0 = No change.
nl_after_access_spec                     = 0        # number

# The number of newlines between a function def and the function comment.
# 0 = No change.
nl_comment_func_def                      = 0        # number

# The number of newlines after a try-catch-finally block that isn't followed by a brace close.
# 0 = No change.
nl_after_try_catch_finally               = 0        # number

# The number of newlines before and after a property, indexer or event decl.
# 0 = No change.
nl_around_cs_property                    = 0        # number

# The number of newlines between the get/set/add/remove handlers in C#.
# 0 = No change.
nl_between_get_set                       = 0        # number

# Add or remove newline between C# property and the '{'
nl_property_brace                        = ignore   # ignore/add/remove/force

# Whether to remove blank lines after '{'
eat_blanks_after_open_brace              = true    # false/true

# Whether to remove blank lines before '}'
eat_blanks_before_close_brace            = true    # false/true

# How aggressively to remove extra newlines not in preproc.
# 0: No change
# 1: Remove most newlines not handled by other config
# 2: Remove all newlines and reformat completely by config
nl_remove_extra_newlines                 = 0        # number

# Whether to put a blank line before 'return' statements, unless after an open brace.
nl_before_return                         = false    # false/true

# Whether to put a blank line after 'return' statements, unless followed by a close brace.
nl_after_return                          = false    # false/true

# Whether to put a newline after a Java annotation statement.
# Only affects annotations that are after a newline.
nl_after_annotation                      = ignore   # ignore/add/remove/force

# Controls the newline between two annotations.
nl_between_annotation                    = ignore   # ignore/add/remove/force

#
# Code modifying options (non-whitespace)
#

# Add or remove braces on single-line 'do' statement
mod_full_brace_do                        = force   # ignore/add/remove/force

# Add or remove braces on single-line 'for' statement
mod_full_brace_for                       = force   # ignore/add/remove/force

# Add or remove braces on single-line function definitions. (Pawn)
mod_full_brace_function                  = force   # ignore/add/remove/force

# Add or remove braces on single-line 'if' statement. Will not remove the braces if they contain an 'else'.
mod_full_brace_if                        = force   # ignore/add/remove/force

# Make all if/elseif/else statements in a chain be braced or not. Overrides mod_full_brace_if.
# If any must be braced, they are all braced.  If all can be unbraced, then the braces are removed.
mod_full_brace_if_chain                  = 0    # false/true

# Don't remove braces around statements that span N newlines
mod_full_brace_nl                        = 0        # number

# Add or remove braces on single-line 'while' statement
mod_full_brace_while                     = force   # ignore/add/remove/force

# Add or remove braces on single-line 'using ()' statement
mod_full_brace_using                     = ignore   # ignore/add/remove/force

# Add or remove unnecessary paren on 'return' statement
mod_paren_on_return                      = ignore   # ignore/add/remove/force

# Whether to change optional semicolons to real semicolons
mod_pawn_semicolon                       = false    # false/true

# Add parens on 'while' and 'if' statement around bools
mod_full_paren_if_bool                   = false    # false/true

# Whether to remove superfluous semicolons
mod_remove_extra_semicolon               = false    # false/true

# If a function body exceeds the specified number of newlines and doesn't have a comment after
# the close brace, a comment will be added.
mod_add_long_function_closebrace_comment = 0        # number

# If a namespace body exceeds the specified number of newlines and doesn't have a comment after
# the close brace, a comment will be added.
mod_add_long_namespace_closebrace_comment = 0        # number
# If a switch body exceeds the specified number of newlines and doesn't have a comment after
# the close brace, a comment will be added.
mod_add_long_switch_closebrace_comment   = 0        # number

# If an #ifdef body exceeds the specified number of newlines and doesn't have a comment after
# the #endif, a comment will be added.
mod_add_long_ifdef_endif_comment         = 0        # number

# If an #ifdef or #else body exceeds the specified number of newlines and doesn't have a comment after
# the #else, a comment will be added.
mod_add_long_ifdef_else_comment          = 0        # number

# If TRUE, will sort consecutive single-line 'import' statements [Java, D]
mod_sort_import                          = false    # false/true

# If TRUE, will sort consecutive single-line 'using' statements [C#]
mod_sort_using                           = true    # false/true

# If TRUE, will sort consecutive single-line '#include' statements [C/C++] and '#import' statements [Obj-C]
# This is generally a bad idea, as it may break your code.
mod_sort_include                         = false    # false/true

# If TRUE, it will move a 'break' that appears after a fully braced 'case' before the close brace.
mod_move_case_break                      = false    # false/true

# Will add or remove the braces around a fully braced case statement.
# Will only remove the braces if there are no variable declarations in the block.
mod_case_brace                           = ignore   # ignore/add/remove/force

# If TRUE, it will remove a void 'return;' that appears as the last statement in a function.
mod_remove_empty_return                  = false    # false/true

#
# Comment modifications
#

# Try to wrap comments at cmt_width columns
cmt_width                                = 0        # number

# Set the comment reflow mode (default: 0)
# 0: no reflowing (apart from the line wrapping due to cmt_width)
# 1: no touching at all
# 2: full reflow
cmt_reflow_mode                          = 0        # number

# If false, disable all multi-line comment changes, including cmt_width. keyword substitution, and leading chars.
# Default is true.
cmt_indent_multi                         = true     # false/true

# Whether to group c-comments that look like they are in a block
cmt_c_group                              = false    # false/true

# Whether to put an empty '/*' on the first line of the combined c-comment
cmt_c_nl_start                           = false    # false/true

# Whether to put a newline before the closing '*/' of the combined c-comment
cmt_c_nl_end                             = false    # false/true

# Whether to group cpp-comments that look like they are in a block
cmt_cpp_group                            = false    # false/true

# Whether to put an empty '/*' on the first line of the combined cpp-comment
cmt_cpp_nl_start                         = false    # false/true

# Whether to put a newline before the closing '*/' of the combined cpp-comment
cmt_cpp_nl_end                           = false    # false/true

# Whether to change cpp-comments into c-comments
cmt_cpp_to_c                             = false    # false/true

# Whether to put a star on subsequent comment lines
cmt_star_cont                            = false    # false/true

# The number of spaces to insert at the start of subsequent comment lines
cmt_sp_before_star_cont                  = 0        # number

# The number of spaces to insert after the star on subsequent comment lines
cmt_sp_after_star_cont                   = 0        # number

# For multi-line comments with a '*' lead, remove leading spaces if the first and last lines of
# the comment are the same length. Default=True
cmt_multi_check_last                     = true     # false/true

# The filename that contains text to insert at the head of a file if the file doesn't start with a C/C++ comment.
# Will substitute $(filename) with the current file's name.
cmt_insert_file_header                   = ""         # string

# The filename that contains text to insert at the end of a file if the file doesn't end with a C/C++ comment.
# Will substitute $(filename) with the current file's name.
cmt_insert_file_footer                   = ""         # string

# The filename that contains text to insert before a function implementation if the function isn't preceded with a C/C++ comment.
# Will substitute $(function) with the function name and $(javaparam) with the javadoc @param and @return stuff.
# Will also substitute $(fclass) with the class name: void CFoo::Bar() { ... }
cmt_insert_func_header                   = ""         # string

# The filename that contains text to insert before a class if the class isn't preceded with a C/C++ comment.
# Will substitute $(class) with the class name.
cmt_insert_class_header                  = ""         # string

# The filename that contains text to insert before a Obj-C message specification if the method isn't preceeded with a C/C++ comment.
# Will substitute $(message) with the function name and $(javaparam) with the javadoc @param and @return stuff.
cmt_insert_oc_msg_header                 = ""         # string

# If a preprocessor is encountered when stepping backwards from a function name, then
# this option decides whether the comment should be inserted.
# Affects cmt_insert_oc_msg_header, cmt_insert_func_header and cmt_insert_class_header.
cmt_insert_before_preproc                = false    # false/true

#
# Preprocessor options
#

# Control indent of preprocessors inside #if blocks at brace level 0
pp_indent                                = force   # ignore/add/remove/force

# Whether to indent #if/#else/#endif at the brace level (true) or from column 1 (false)
pp_indent_at_level                       = false    # false/true

# If pp_indent_at_level=false, specifies the number of columns to indent per level. Default=1.
pp_indent_count                          = 1        # number

# Add or remove space after # based on pp_level of #if blocks
pp_space                                 = ignore   # ignore/add/remove/force

# Sets the number of spaces added with pp_space
pp_space_count                           = 0        # number

# The indent for #region and #endregion in C# and '#pragma region' in C/C++
pp_indent_region                         = 0        # number

# Whether to indent the code between #region and #endregion
pp_region_indent_code                    = false    # false/true

# If pp_indent_at_level=true, sets the indent for #if, #else, and #endif when not at file-level
pp_indent_if                             = 0        # number

# Control whether to indent the code between #if, #else and #endif when not at file-level
pp_if_indent_code                        = false    # false/true

# Whether to indent '#define' at the brace level (true) or from column 1 (false)
pp_define_at_level                       = false    # false/true
0707010000000F000081A4000000000000000000000001690B16EB000001D1000000000000000000000000000000000000002E00000000SwayNotificationCenter-0.12.3/.vala-lint.conf[Checks]
block-opening-brace-space-before=error
double-semicolon=error
double-spaces=error
ellipsis=off
line-length=error
naming-convention=error
no-space=error
note=warn
space-before-paren=error
use-of-tabs=error
trailing-newlines=error
trailing-whitespace=error
unnecessary-string-template=error

[Disabler]
disable-by-inline-comments=true

[line-length]
max-line-length=100
ignore-comments=true

[naming-convention]
exceptions=UUID,

[note]
keywords=TODO,FIXME,
07070100000010000081A4000000000000000000000001690B16EB0000894C000000000000000000000000000000000000002600000000SwayNotificationCenter-0.12.3/COPYING                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

07070100000011000081A4000000000000000000000001690B16EB000032EC000000000000000000000000000000000000002800000000SwayNotificationCenter-0.12.3/README.md# SwayNotificationCenter

[![Check PKGBUILD builds for Arch.](https://github.com/ErikReider/SwayNotificationCenter/actions/workflows/PKGBUILD-build.yml/badge.svg)](https://github.com/ErikReider/SwayNotificationCenter/actions/workflows/PKGBUILD-buildd.yml)
[![Check build for Fedora.](https://github.com/ErikReider/SwayNotificationCenter/actions/workflows/fedora-build.yml/badge.svg)](https://github.com/ErikReider/SwayNotificationCenter/actions/workflows/fedora-build.yml)
[![Check build for latest Ubuntu LTS.](https://github.com/ErikReider/SwayNotificationCenter/actions/workflows/ubuntu-build.yml/badge.svg)](https://github.com/ErikReider/SwayNotificationCenter/actions/workflows/ubuntu-build.yml)
[![Linting](https://github.com/ErikReider/SwayNotificationCenter/actions/workflows/linting.yml/badge.svg)](https://github.com/ErikReider/SwayNotificationCenter/actions/workflows/linting.yml)

A simple notification daemon with a GTK gui for notifications and the control center

*Note: SwayNotificationCenter only supports Desktops / Window Managers that
support `wlr_layer_shell_unstable_v1` like Sway or anything wlroots based*

*Note 2: SwayNotificationCenter does not support third-party GTK3 themes and is
only tested with the default GTK **Adwaita** theme. Usage of any third-party
theme might require extra tweaks to the default CSS style file*

## Demo

https://github.com/user-attachments/assets/5c054ac3-90bb-483e-a8f2-5af191805f04

## Table of Contents

  * [Want to show off your sick config?](#want-to-show-off-your-sick-config)
  * [Features](#features)
  * [Available Widgets](#available-widgets)
  * [Planned Features](#planned-features)
  * [Install](#install)
     * [Arch](#arch)
     * [Fedora](#fedora)
     * [Fedora Silverblue (and other rpm-ostree variants)](#fedora-silverblue-and-other-rpm-ostree-variants)
     * [Gentoo](#gentoo)
     * [OpenSUSE Tumbleweed](#opensuse-tumbleweed)
     * [Ubuntu](#ubuntu)
     * [Debian](#debian)
     * [Guix](#guix)
     * [rde](#rde)
     * [Other](#other)
  * [Sway Usage](#sway-usage)
  * [Run](#run)
  * [Control Center Shortcuts](#control-center-shortcuts)
  * [Configuring](#configuring)
    * [Toggle Buttons](#toggle-buttons)
  * [Notification Inhibition](#notification-inhibition)
  * [Scripting](#scripting)
     * [Disable scripting](#disable-scripting)
  * [i3status-rs Example](#i3status-rs-example)
  * [Waybar Example](#waybar-example)
  * [Debugging Environment Variables](#debugging-environment-variables)

## Want to show off your sick config?

Post your setup here: [Config flex 💪](https://github.com/ErikReider/SwayNotificationCenter/discussions/183)

<p>
  <img height="500" alt="Config" src="https://github.com/user-attachments/assets/5993cbb2-a54d-4d1c-9289-b7e2b9d0a09f" />
</p>
<p>
  <em>
    Example of my config
    (<a href="https://github.com/ErikReider/Linux/tree/master/dotfiles/.config/swaync">link</a>)
  </em>
</p>

## Features

- Grouped notifications
- Keyboard shortcuts
- Notification body markup with image support
- Inline replies
- A panel to view previous notifications
- Show album art for notifications like Spotify
- Do not disturb
- Notification action filtering
- Inhibiting notifications through DBUS or client
- Restores previous Do not disturb value after restart
- Click notification to execute default action
- Show alternative notification actions
- Copy detected 2FA codes to clipboard
- Customization through a CSS file
- Trackpad/mouse gesture to close notification
- The same features as any other basic notification daemon
- Basic configuration through a JSON config file
- Hot-reload config through `swaync-client`
- Customizable widgets
- Select the preferred monitor to display on (with swaync-client command for scripting)

## Available Widgets

These widgets can be customized, added, removed and even reordered

- Title
- Do Not Disturb
- Notifications (Will always be visible)
- Label
- Mpris (Media player controls for Spotify, Firefox, Chrome, etc...)
- Menubar with dropdown and buttons
- Button grid
- Volume slider using PulseAudio
- Backlight slider

## Planned Features

- Slick animations 😎

## Install

### Alpine Linux

```zsh
apk add swaync
````

### Arch

```zsh
sudo pacman -S swaync
```

Alternatively, [swaync-git](https://aur.archlinux.org/packages/swaync-git/) is available on the AUR.

### Fedora

The package is available on COPR:

```zsh
dnf copr enable erikreider/SwayNotificationCenter
# Or latest stable release or -git package
dnf install SwayNotificationCenter
dnf install SwayNotificationCenter-git
```

### Fedora Silverblue (and other rpm-ostree variants)

The package can be layered over the base image after adding the Copr repo as an ostree repo:

```zsh
sudo curl -sL -o /etc/yum.repos.d/_copr:copr.fedorainfracloud.org:erikreider:SwayNotificationCenter.repo https://copr.fedorainfracloud.org/coprs/erikreider/SwayNotificationCenter/repo/fedora-$(rpm -E %fedora)/erikreider-SwayNotificationCenter-fedora-$(rpm -E %fedora).repo 
rpm-ostree install SwayNotificationCenter
```

### Gentoo

An **unofficial** ebuild is available in [GURU](https://github.com/gentoo/guru)

```zsh
eselect repository enable guru
emaint sync --repo guru
emerge --ask gui-apps/swaync
```

### OpenSUSE Tumbleweed

```zsh
sudo zypper install SwayNotificationCenter
```

### Ubuntu

Lunar and later:

```zsh
sudo apt install sway-notification-center
```

### Debian

Bookworm and later:

```zsh
sudo apt install sway-notification-center
```

### Guix

The simplest way is to install it to user's profile:

```zsh
guix install swaynotificationcenter
```

But we recommend to use [Guix Home](https://guix.gnu.org/manual/devel/en/html_node/Home-Configuration.html) to manage packages and their configurations declaratively.

### rde

```scm
(use-modules (rde features wm))

;; Include the following code into the list of your rde features:
(feature-swaynotificationcenter)
```

### Other

#### Dependencies

- `vala >= 0.56`
- `meson`
- `blueprint-compiler`
- `git`
- `scdoc`
- `sassc`
- `gtk4`
- `gtk4-layer-shell`
- `dbus`
- `glib2`
- `gobject-introspection`
- `libgee`
- `json-glib`
- `libadwaita`
- `gvfs`
- `granite7`

##### Optional Dependencies

- `libpulse` (requires meson build options change)
- `libnotify`

```zsh
meson setup build --prefix=/usr
ninja -C build
meson install -C build
```

## Sway Usage

```ini
# Notification Daemon
exec swaync

# Toggle control center
bindsym $mod+Shift+n exec swaync-client -t -sw
```

## Run

To start the daemon (remember to kill any other notification daemon before running)

```zsh
./build/src/swaync
```

To toggle the panel

```zsh
./build/src/swaync-client -t
```

To reload the config

```zsh
./build/src/swaync-client -R
```

To reload css after changes

```zsh
./build/src/swaync-client -rs
```

## Control Center Shortcuts

- Up/Down: Navigate notifications
- Home: Navigate to the latest notification
- End: Navigate to the oldest notification
- Escape/Caps_Lock: Close notification panel
- Return: Execute default action or close notification if none
- Delete/BackSpace: Close notification
- Shift+C: Close all notifications
- Shift+D: Toggle Do Not Disturb
- Buttons 1-9: Execute alternative actions
- Left click button / actions: Activate notification action
- Middle/Right click notification: Close notification

## Configuring

The main config file is located in `/etc/xdg/swaync/config.json`. Copy it over
to your `.config/swaync/` folder to customize without needing root access.
See `swaync(5)` man page for more information

To reload the config, you'll need to run `swaync-client --reload-config`

The main CSS style file is located in `/etc/xdg/swaync/style.css`. Copy it over
to your `~/.config/swaync/` folder to customize without needing root access. For
more advanced/larger themes, I recommend that you use the SCSS files from source
and customize them instead. To use the SCSS files, compile with `sassc`.

**Tip**: running swaync with `GTK_DEBUG=interactive swaync` will open a inspector
window that'll allow you to see all of the CSS classes + other information.

## Toggle Buttons

To add toggle buttons to your control center you can set the "type" in any acton to "toggle".
The toggle button supports different commands depending on the state of the button and
an "update-command" to update the state in case of changes from outside swaync. The update-command
is called every time the control center is opened.
The active toggle button also gains the css-class ".toggle:checked"

`config.json` example:

```jsonc
{
  "buttons-grid": { // also works with actions in menubar widget
    "actions": [
      {
        "label": "WiFi",
        "type": "toggle",
        "active": true,
        "command": "sh -c '[[ $SWAYNC_TOGGLE_STATE == true ]] && nmcli radio wifi on || nmcli radio wifi off'",
        "update-command": "sh -c '[[ $(nmcli radio wifi) == \"enabled\" ]] && echo true || echo false'"
      }
    ]
  }
}
```

## Notification Inhibition

Notifications can be inhibited through the provided `swaync-client` executable
or through the DBus interface `org.erikreider.swaync.cc`.

Here's an example of notification inhibition while screen sharing through
`xdg-desktop-portal-wlr`

```conf
# xdg-desktop-portal-wlr config
[screencast]
exec_before=swaync-client --inhibitor-add "xdg-desktop-portal-wlr"
exec_after=swaync-client --inhibitor-remove "xdg-desktop-portal-wlr"
```

## Scripting

Scripting rules and logic:

. <b>Only one</b> script can be fired per notification
. Each script requires `exec` and at least one of the other properties
. All listed properties must match the notification for the script to be ran
. If any of the properties doesn't match, the script will be skipped
. If a notification doesn't include one of the properties, that property will
be skipped
· If a script has `run-on` set to `action`, the script will only run when an
action is taken on the notification

More information can be found in the `swaync(5)` man page

Notification information can be printed into a terminal by running
`G_MESSAGES_DEBUG=all swaync` (when a notification appears).

Config properties:

```jsonc
{
  "scripts": {
    "example-script": {
      "exec": "Your shell command or script here...",
      "app-name": "Notification app-name Regex",
      "summary": "Notification summary Regex",
      "body": "Notification body Regex",
      "urgency": "Low or Normal or Critical",
      "category": "Notification category Regex"
    }
  }
  // other non scripting properties...
}
```

`config.json` example:

```jsonc
{
  "scripts": {
    // This script will only run when Spotify sends a notification containing
    // that exact summary and body
    "example-script": {
      "exec": "/path/to/myRickRollScript.sh",
      "app-name": "Spotify",
      "summary": "Never Gonna Give You Up",
      "body": "Rick Astley - Whenever You Need Somebody"
    }
  }
  // other non scripting properties...
}
```

### Disable scripting

To completely disable scripting, the project needs to be built like so:

```zsh
meson build -Dscripting=false
ninja -C build
meson install -C build
```

## i3status-rs Example

> **Note** Ths requires i3status-rs version 0.31.0+

i3status-rs config

```toml
[[block]]
block = "notify"
format = " $icon {($notification_count.eng(w:1)) |}"
driver = "swaync"
[[block.click]]
button = "left"
action = "show"
[[block.click]]
button = "right"
action = "toggle_paused"
```

## Waybar Example

This example requires a [Nerd Fonts](https://www.nerdfonts.com/) font to get the icons looking right

Waybar config

```json
  "custom/notification": {
    "tooltip": true,
    "format": "<span size='16pt'>{icon}</span>",
    "format-icons": {
      "notification": "ó±…«",
      "none": "󰂜",
      "dnd-notification": "ó°‚ ",
      "dnd-none": "󰪓",
      "inhibited-notification": "ó°‚›",
      "inhibited-none": "󰪑",
      "dnd-inhibited-notification": "ó°‚›",
      "dnd-inhibited-none": "󰪑"
    },
    "return-type": "json",
    "exec-if": "which swaync-client",
    "exec": "swaync-client -swb",
    "on-click": "swaync-client -t -sw",
    "on-click-right": "swaync-client -d -sw",
    "escape": true
  },
```

Waybar css file

```css
#custom-notification {
  font-family: "NotoSansMono Nerd Font";
}
```

Alternatively, the number of notifications can be shown by adding `{0}` anywhere in the `format` field in the Waybar config

```jsonc
  "custom/notification": {
    "format": "{0} {icon}",
    // ...
  },
```

## Debugging Environment Variables

- `G_MESSAGES_DEBUG=all`: Displays all of the debug messages.
- `GTK_DEBUG=interactive`: Opens the GTK Inspector.
- `G_ENABLE_DIAGNOSTIC=1`: If set to a non-zero value, this environment variable
  enables diagnostic messages, like deprecation messages for GObject properties
  and signals.
- `G_DEBUG=fatal_criticals` or `G_DEBUG=fatal_warnings`: Causes GLib to abort
  the program at the first call to g_warning() or g_critical().

More can be read [here](https://www.manpagez.com/html/glib/glib-2.56.0/glib-running.php)
07070100000012000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002800000000SwayNotificationCenter-0.12.3/build-aux07070100000013000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002E00000000SwayNotificationCenter-0.12.3/build-aux/meson07070100000014000081ED000000000000000000000001690B16EB00000196000000000000000000000000000000000000003D00000000SwayNotificationCenter-0.12.3/build-aux/meson/postinstall.py#!/usr/bin/env python3

from os import environ, path
from subprocess import call

prefix = environ.get('MESON_INSTALL_PREFIX', '/usr/local')
datadir = path.join(prefix, 'share')
destdir = environ.get('DESTDIR', '')

# Package managers set this so we don't need to run
if not destdir:
    print('Compiling GSettings schemas...')
    call(['glib-compile-schemas', path.join(datadir, 'glib-2.0', 'schemas')])
07070100000015000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002C00000000SwayNotificationCenter-0.12.3/build-scripts07070100000016000081A4000000000000000000000001690B16EB0000001C000000000000000000000000000000000000003700000000SwayNotificationCenter-0.12.3/build-scripts/.gitignore./*
!PKGBUILD*
!*.rpkg.spec
07070100000017000081A4000000000000000000000001690B16EB0000053A000000000000000000000000000000000000003900000000SwayNotificationCenter-0.12.3/build-scripts/PKGBUILD-git# vim: ft=sh
# Maintainer: Erik Reider <erik.reider@protonmail.com>
pkgname=swaync-git
_pkgname=swaync
pkgver=r645.0998431
pkgrel=1
pkgdesc="A simple notification daemon with a GTK panel for checking previous notifications like other DEs"
url="https://github.com/ErikReider/SwayNotificationCenter"
arch=(
    'x86_64'
    'aarch64' # ARM v8 64-bit
    'armv7h'  # ARM v7 hardfloat
)
license=('GPL3')
depends=("gtk4" "gtk4-layer-shell>=1.0.4" "dbus" "glib2" "gobject-introspection" "libgee" "json-glib" "libpulse" "gvfs" "libnotify" "granite7" "blueprint-compiler" "libadwaita")
conflicts=("swaync" "swaync-client")
provides=("swaync" "swaync-client" "notification-daemon")
makedepends=("vala>=0.56" meson git scdoc sassc)
source=("$_pkgname::git+$url")
sha256sums=('SKIP')

pkgver() {
    cd $_pkgname
    printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
}

prepare() {
    cd $_pkgname
    git checkout main
}

build() {
    # Very important to be in the directory! Otherwise somehow breaks linking to the .ui files
    cd $_pkgname
    arch-meson build -Dscripting=true
    ninja -C build
}

package() {
    cd $_pkgname
    DESTDIR="$pkgdir/" ninja -C build install
    install -Dm644 "COPYING" -t "$pkgdir/usr/share/licenses/$pkgname"
    install -Dm644 "README.md" -t "$pkgdir/usr/share/doc/$pkgname"
}
07070100000018000081A4000000000000000000000001690B16EB00000E3C000000000000000000000000000000000000004100000000SwayNotificationCenter-0.12.3/build-scripts/swaync-git.rpkg.spec# vim: syntax=spec
%global alt_pkg_name swaync

Name:       {{{ git_repo_name }}}-git
Version:    {{{ git_repo_release lead="$(git describe --tags --abbrev=0)" }}}
Release:    {{{ echo -n "$(git rev-list --all --count)" }}}%{?dist}
Summary:    Notification daemon with GTK GUI
Provides:   desktop-notification-daemon
Provides:   sway-notification-center = %{version}-%{release}
Provides:   %{alt_pkg_name} = %{version}-%{release}
Provides:   %{alt_pkg_name}-git = %{version}-%{release}
License:    GPLv3
URL:        https://github.com/ErikReider/SwayNotificationCenter
VCS:        {{{ git_repo_vcs }}}
Source:     {{{ git_repo_pack }}}

BuildRequires:  meson >= 1.5.1
BuildRequires:  vala >= 0.56
BuildRequires:  scdoc
BuildRequires:  pkgconfig(gtk4) >= 4.16
BuildRequires:  pkgconfig(gtk4-layer-shell-0) >= 1.0.4
BuildRequires:  pkgconfig(json-glib-1.0) >= 1.0
BuildRequires:  pkgconfig(libadwaita-1) >= 1.6.5
BuildRequires:  pkgconfig(glib-2.0) >= 2.50
BuildRequires:  pkgconfig(gobject-introspection-1.0) >= 1.68
BuildRequires:  pkgconfig(gee-0.8) >= 0.20
BuildRequires:  pkgconfig(bash-completion)
BuildRequires:  pkgconfig(fish)
BuildRequires:  pkgconfig(libpulse)
BuildRequires:  pkgconfig(granite-7)
BuildRequires:  systemd-devel
BuildRequires:  systemd
BuildRequires:  sassc
BuildRequires:  blueprint-compiler >= 0.16

Requires:       gvfs
Requires:       libnotify
Requires:       dbus
%{?systemd_requires}

%description
A simple notification daemon with a GTK gui for notifications and the control center

%package bash-completion
BuildArch:      noarch
Summary:        Bash completion files for %{name}
Provides:       %{alt_pkg_name}-bash-completion = %{version}-%{release}

Requires:       bash-completion
Requires:       %{name} = %{version}-%{release}

%description bash-completion
This package installs Bash completion files for %{name}

%package zsh-completion
BuildArch:      noarch
Summary:        Zsh completion files for %{name}
Provides:       %{alt_pkg_name}-zsh-completion = %{version}-%{release}

Requires:       zsh
Requires:       %{name} = %{version}-%{release}

%description zsh-completion
This package installs Zsh completion files for %{name}

%package fish-completion
BuildArch:      noarch
Summary:        Fish completion files for %{name}
Provides:       %{alt_pkg_name}-fish-completion = %{version}-%{release}

Requires:       fish
Requires:       %{name} = %{version}-%{release}

%description fish-completion
This package installs Fish completion files for %{name}

%prep
{{{ git_repo_setup_macro }}}

%build
%meson
%meson_build

%install
%meson_install

%post
%systemd_user_post swaync.service

%preun
%systemd_user_preun swaync.service

%files
%doc README.md
%{_bindir}/swaync-client
%{_bindir}/swaync
%license COPYING
%config(noreplace) %{_sysconfdir}/xdg/swaync/configSchema.json
%config(noreplace) %{_sysconfdir}/xdg/swaync/config.json
%config(noreplace) %{_sysconfdir}/xdg/swaync/style.css
%{_userunitdir}/swaync.service
%{_datadir}/dbus-1/services/org.erikreider.swaync.service
%{_datadir}/glib-2.0/schemas/org.erikreider.swaync.gschema.xml
%{_mandir}/man1/swaync-client.1.gz
%{_mandir}/man1/swaync.1.gz
%{_mandir}/man5/swaync.5.gz

%files bash-completion
%{_datadir}/bash-completion/completions/swaync
%{_datadir}/bash-completion/completions/swaync-client

%files zsh-completion
%{_datadir}/zsh/site-functions/_swaync
%{_datadir}/zsh/site-functions/_swaync-client

%files fish-completion
%{_datadir}/fish/vendor_completions.d/swaync-client.fish
%{_datadir}/fish/vendor_completions.d/swaync.fish

# Changelog will be empty until you make first annotated Git tag.
%changelog
{{{ git_repo_changelog }}}
07070100000019000081A4000000000000000000000001690B16EB00000D97000000000000000000000000000000000000003D00000000SwayNotificationCenter-0.12.3/build-scripts/swaync.rpkg.spec# vim: syntax=spec
%global alt_pkg_name swaync

Name:       {{{ git_repo_name }}}
Version:    0.12.3
Release:    1%{?dist}
Summary:    Notification daemon with GTK GUI
Provides:   desktop-notification-daemon
Provides:   sway-notification-center = %{version}-%{release}
Provides:   %{alt_pkg_name} = %{version}-%{release}
License:    GPLv3
URL:        https://github.com/ErikReider/SwayNotificationCenter
VCS:        {{{ git_repo_vcs }}}
Source:     {{{ git_repo_pack }}}

BuildRequires:  meson >= 1.5.1
BuildRequires:  vala >= 0.56
BuildRequires:  scdoc
BuildRequires:  pkgconfig(gtk4) >= 4.16
BuildRequires:  pkgconfig(gtk4-layer-shell-0) >= 1.0.4
BuildRequires:  pkgconfig(json-glib-1.0) >= 1.0
BuildRequires:  pkgconfig(libadwaita-1) >= 1.6.5
BuildRequires:  pkgconfig(glib-2.0) >= 2.50
BuildRequires:  pkgconfig(gobject-introspection-1.0) >= 1.68
BuildRequires:  pkgconfig(gee-0.8) >= 0.20
BuildRequires:  pkgconfig(bash-completion)
BuildRequires:  pkgconfig(fish)
BuildRequires:  pkgconfig(libpulse)
BuildRequires:  pkgconfig(granite-7)
BuildRequires:  systemd-devel
BuildRequires:  systemd
BuildRequires:  sassc
BuildRequires:  blueprint-compiler >= 0.16

Requires:       gvfs
Requires:       libnotify
Requires:       dbus
%{?systemd_requires}

%description
A simple notification daemon with a GTK gui for notifications and the control center

%package bash-completion
BuildArch:      noarch
Summary:        Bash completion files for %{name}
Provides:       %{alt_pkg_name}-bash-completion = %{version}-%{release}

Requires:       bash-completion
Requires:       %{name} = %{version}-%{release}

%description bash-completion
This package installs Bash completion files for %{name}

%package zsh-completion
BuildArch:      noarch
Summary:        Zsh completion files for %{name}
Provides:       %{alt_pkg_name}-zsh-completion = %{version}-%{release}

Requires:       zsh
Requires:       %{name} = %{version}-%{release}

%description zsh-completion
This package installs Zsh completion files for %{name}

%package fish-completion
BuildArch:      noarch
Summary:        Fish completion files for %{name}
Provides:       %{alt_pkg_name}-fish-completion = %{version}-%{release}

Requires:       fish
Requires:       %{name} = %{version}-%{release}

%description fish-completion
This package installs Fish completion files for %{name}

%prep
{{{ git_repo_setup_macro }}}

%build
%meson
%meson_build

%install
%meson_install

%post
%systemd_user_post swaync.service

%preun
%systemd_user_preun swaync.service

%files
%doc README.md
%{_bindir}/swaync-client
%{_bindir}/swaync
%license COPYING
%config(noreplace) %{_sysconfdir}/xdg/swaync/configSchema.json
%config(noreplace) %{_sysconfdir}/xdg/swaync/config.json
%config(noreplace) %{_sysconfdir}/xdg/swaync/style.css
%{_userunitdir}/swaync.service
%{_datadir}/dbus-1/services/org.erikreider.swaync.service
%{_datadir}/glib-2.0/schemas/org.erikreider.swaync.gschema.xml
%{_mandir}/man1/swaync-client.1.gz
%{_mandir}/man1/swaync.1.gz
%{_mandir}/man5/swaync.5.gz

%files bash-completion
%{_datadir}/bash-completion/completions/swaync
%{_datadir}/bash-completion/completions/swaync-client

%files zsh-completion
%{_datadir}/zsh/site-functions/_swaync
%{_datadir}/zsh/site-functions/_swaync-client

%files fish-completion
%{_datadir}/fish/vendor_completions.d/swaync-client.fish
%{_datadir}/fish/vendor_completions.d/swaync.fish

# Changelog will be empty until you make first annotated Git tag.
%changelog
{{{ git_repo_changelog }}}
0707010000001A000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002A00000000SwayNotificationCenter-0.12.3/completions0707010000001B000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002F00000000SwayNotificationCenter-0.12.3/completions/bash0707010000001C000081A4000000000000000000000001690B16EB0000034B000000000000000000000000000000000000003600000000SwayNotificationCenter-0.12.3/completions/bash/swaync_swaync() {
    local cur prev
    _get_comp_words_by_ref cur prev

    short=(
        -h
        -v
        -s
        -c
    )

    long=(
        --help
        --version
        --style
        --config
    )
    case $prev in
        -s | --style)
            _filedir
            return
            ;;
        -c | --config)
            _filedir
            return
            ;;
    esac

    if [[ $cur == --* ]]; then
        COMPREPLY=($(compgen -W "${long[*]}" -- "$cur"))
    elif [[ $cur == -* ]]; then
        COMPREPLY=($(compgen -W "${short[*]}" -- "$cur"))
        COMPREPLY+=($(compgen -W "${long[*]}" -- "$cur"))
    else
        COMPREPLY=($(compgen -W "${short[*]}" -- "$cur"))
        COMPREPLY+=($(compgen -W "${long[*]}" -- "$cur"))
        COMPREPLY+=($(compgen -c -- "$cur"))
    fi

} && complete -F _swaync swaync
0707010000001D000081A4000000000000000000000001690B16EB00000623000000000000000000000000000000000000003D00000000SwayNotificationCenter-0.12.3/completions/bash/swaync-client_swaync-client() {
    local cur prev
    _get_comp_words_by_ref cur prev

    short=(
        -h
        -v
        -R
        -rs
        -t
        -op
        -cp
        -d
        -D
        -dn
        -df
        -I
        -In
        -Ia
        -Ir
        -Ic
        -c
        -C
        -sw
        -s
        -swb
    )

    long=(
        --help
        --version
        --reload-config
        --reload-css
        --toggle-panel
        --open-panel
        --close-panel
        --toggle-dnd
        --get-dnd
        --dnd-on
        --dnd-off
        --get-inhibited
        --get-num-inhibitors
        --inhibitor-add
        --inhibitor-remove
        --inhibitors-clear
        --count
        --hide-latest
        --hide-all
        --close-latest
        --close-all
        --skip-wait
        --subscribe
        --subscribe-waybar
        --change-cc-monitor
        --change-noti-monitor
    )
    case $prev in
        -s | --style)
            _filedir
            return
            ;;
        -c | --config)
            _filedir
            return
            ;;
    esac

    if [[ $cur == --* ]]; then
        COMPREPLY=($(compgen -W "${long[*]}" -- "$cur"))
    elif [[ $cur == -* ]]; then
        COMPREPLY=($(compgen -W "${short[*]}" -- "$cur"))
        COMPREPLY+=($(compgen -W "${long[*]}" -- "$cur"))
    else
        COMPREPLY=($(compgen -W "${short[*]}" -- "$cur"))
        COMPREPLY+=($(compgen -W "${long[*]}" -- "$cur"))
        COMPREPLY+=($(compgen -c -- "$cur"))
    fi

} && complete -F _swaync-client swaync-client
0707010000001E000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002F00000000SwayNotificationCenter-0.12.3/completions/fish0707010000001F000081A4000000000000000000000001690B16EB00000AA1000000000000000000000000000000000000004200000000SwayNotificationCenter-0.12.3/completions/fish/swaync-client.fishcomplete -f -c swaync-client
complete -c swaync-client -s h -l help --description "Show help options"
complete -c swaync-client -s v -l version --description "Prints version"
complete -c swaync-client -s R -l reload-config --description "Reload the config file" -r
complete -c swaync-client -s rs -l reload-css --description "Reload the css file. Location change requires restart" -r
complete -c swaync-client -s t -l toggle-panel --description "Toggle the notification panel" -r
complete -c swaync-client -s op -l open-panel --description "Opens the notification panel" -r
complete -c swaync-client -s cp -l close-panel --description "Closes the notification panel" -r
complete -c swaync-client -s d -l toggle-dnd --description "Toggle and print the current dnd state" -r
complete -c swaync-client -s D -l get-dnd --description "Print the current dnd state" -r
complete -c swaync-client -s dn -l dnd-on --description "Turn dnd on and print the new dnd state" -r
complete -c swaync-client -s df -l dnd-off --description "Turn dnd off and print the new dnd state" -r
complete -c swaync-client -s I -l get-inhibited --description "Print if currently inhibited or not" -r
complete -c swaync-client -s In -l get-num-inhibitors --description "Print number of inhibitors" -r
complete -c swaync-client -s Ia -l inhibitor-add --description "Add an inhibitor" -r
complete -c swaync-client -s Ir -l inhibitor-remove --description "Remove an inhibitor" -r
complete -c swaync-client -s Ic -l inhibitors-clear --description "Clears all inhibitors" -r
complete -c swaync-client -s c -l count --description "Print the current notification count" -r
complete -c swaync-client      -l hide-latest --description "Hides latest notification. Still shown in Control Center" -r
complete -c swaync-client      -l hide-all --description "Hides all notifications. Still shown in Control Center" -r
complete -c swaync-client      -l close-latest --description "Closes latest notification" -r
complete -c swaync-client -s C -l close-all --description "Closes all notifications" -r
complete -c swaync-client -s sw -l skip-wait --description "Doesn't wait when swaync hasn't been started" -r
complete -c swaync-client -s s -l subscribe --description "Subscribe to notification add and close events" -r
complete -c swaync-client -s swb -l subscribe-waybar --description "Subscribe to notification add and close events with waybar support. Read README for example" -r
complete -c swaync-client      -l change-cc-monitor --description "Changes the preferred control center monitor (resets on config reload)" -r
complete -c swaync-client      -l change-noti-monitor --description "Changes the preferred notification monitor (resets on config reload)" -r
07070100000020000081A4000000000000000000000001690B16EB00000139000000000000000000000000000000000000003B00000000SwayNotificationCenter-0.12.3/completions/fish/swaync.fishcomplete -f -c swaync
complete -c swaync -s h -l help --description "Show help options"
complete -c swaync -s v -l version --description "Prints version"
complete -c swaync -s s -l style --description "Use a custom Stylesheet file" -r
complete -c swaync -s c -l config --description "Use a custom config file" -r
07070100000021000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002E00000000SwayNotificationCenter-0.12.3/completions/zsh07070100000022000081A4000000000000000000000001690B16EB00000126000000000000000000000000000000000000003600000000SwayNotificationCenter-0.12.3/completions/zsh/_swaync#compdef swaync

_arguments -s \
    '(-h --help)'{-h,--help}'[Show help options]' \
    '(-v --version)'{-v,--version}'[Prints version]' \
    '(-s --style)'{-s,--style}'[Use a custom Stylesheet file]:files:_files' \
    '(-c --config)'{-c,--config}'[Use a custom config file]:files:_files' \
07070100000023000081A4000000000000000000000001690B16EB000008DE000000000000000000000000000000000000003D00000000SwayNotificationCenter-0.12.3/completions/zsh/_swaync-client#compdef swaync-client

_arguments -s \
    '(-h --help)'{-h,--help}'[Show help options]' \
    '(-v --version)'{-v,--version}'[Prints version]' \
    '(-R --reload-config)'{-R,--reload-config}'[Reload the config file]' \
    '(-rs --reload-css)'{-rs,--reload-css}'[Reload the css file. Location change requires restart]' \
    '(-t --toggle-panel)'{-t,--toggle-panel}'[Toggle the notification panel]' \
    '(-op --open-panel)'{-op,--open-panel}'[Opens the notification panel]' \
    '(-cp --close-panel)'{-cp,--close-panel}'[Closes the notification panel]' \
    '(-d --toggle-dnd)'{-d,--toggle-dnd}'[Toggle and print the current dnd state]' \
    '(-D --get-dnd)'{-D,--get-dnd}'[Print the current dnd state]' \
    '(-dn --dnd-on)'{-dn,--dnd-on}'[Turn dnd on and print the new dnd state]' \
    '(-df --dnd-off)'{-df,--dnd-off}'[Turn dnd off and print the new dnd state]' \
    '(-I --get-inhibited)'{-I,--get-inhibited}'[Print if currently inhibited or not]' \
    '(-In --get-num-inhibitors)'{-In,--get-num-inhibitors}'[Print number of inhibitors]' \
    '(-Ia --inhibitor-add)'{-Ia,--inhibitor-add}'[Add an inhibitor]' \
    '(-Ir --inhibitor-remove)'{-Ir,--inhibitor-remove}'[Remove an inhibitor]' \
    '(-Ic --inhibitors-clear)'{-Ic,--inhibitors-clear}'[Clears all inhibitors]' \
    '(-c --count)'{-c,--count}'[Print the current notification count]' \
    '(--hide-latest)'--hide-latest'[Hides latest notification. Still shown in Control Center]' \
    '(--hide-all)'--hide-all'[Hides all notifications. Still shown in Control Center]' \
    '(--close-latest)'--close-latest'[Closes latest notification]' \
    '(-C --close-all)'{-C,--close-all}'[Closes all notifications]' \
    '(-sw --skip-wait)'{-sw,--skip-wait}"[Doesn't wait when swaync hasn't been started]" \
    '(-s --subscribe)'{-s,--subscribe}'[Subscribe to notification add and close events]' \
    '(-swb --subscribe-waybar)'{-swb,--subscribe-waybar}'[Subscribe to notification add and close events with waybar support. Read README for example]' \
    '(--change-cc-monitor)'--change-cc-monitor'[Changes the preferred control center monitor (resets on config reload)]' \
    '(--change-noti-monitor)'--change-noti-monitor'[Changes the preferred notification monitor (resets on config reload)]' \
07070100000024000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002300000000SwayNotificationCenter-0.12.3/data07070100000025000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002900000000SwayNotificationCenter-0.12.3/data/icons07070100000026000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000003200000000SwayNotificationCenter-0.12.3/data/icons/scalable07070100000027000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000003A00000000SwayNotificationCenter-0.12.3/data/icons/scalable/actions07070100000028000081A4000000000000000000000001690B16EB000002FF000000000000000000000000000000000000005400000000SwayNotificationCenter-0.12.3/data/icons/scalable/actions/swaync-close-symbolic.svg<?xml version="1.0" encoding="UTF-8"?>
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
    <path d="m 4 4 h 1 h 0.03125 c 0.253906 0.011719 0.511719 0.128906 0.6875 0.3125 l 2.28125 2.28125 l 2.3125 -2.28125 c 0.265625 -0.230469 0.445312 -0.304688 0.6875 -0.3125 h 1 v 1 c 0 0.285156 -0.035156 0.550781 -0.25 0.75 l -2.28125 2.28125 l 2.25 2.25 c 0.1875 0.1875 0.28125 0.453125 0.28125 0.71875 v 1 h -1 c -0.265625 0 -0.53125 -0.09375 -0.71875 -0.28125 l -2.28125 -2.28125 l -2.28125 2.28125 c -0.1875 0.1875 -0.453125 0.28125 -0.71875 0.28125 h -1 v -1 c 0 -0.265625 0.09375 -0.53125 0.28125 -0.71875 l 2.28125 -2.25 l -2.28125 -2.28125 c -0.210938 -0.195312 -0.304688 -0.46875 -0.28125 -0.75 z m 0 0" fill="#2e3436"/>
</svg>
07070100000029000081A4000000000000000000000001690B16EB0000033E000000000000000000000000000000000000005700000000SwayNotificationCenter-0.12.3/data/icons/scalable/actions/swaync-collapse-symbolic.svg<?xml version="1.0" encoding="utf-8"?>
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
  <path d="M 4 2.5 C 4 2.234 4.106 1.98 4.293 1.793 C 4.684 1.402 5.317 1.402 5.707 1.793 L 8 4.086 L 10.293 1.793 C 10.684 1.402 11.317 1.402 11.707 1.793 C 11.895 1.98 12 2.234 12 2.5 C 12 2.766 11.895 3.019 11.707 3.207 L 8.707 6.207 C 8.317 6.598 7.684 6.598 7.293 6.207 L 4.293 3.207 C 4.106 3.019 4 2.766 4 2.5 Z" fill="#2e3436"/>
  <path d="M 4 13.5 C 4 13.766 4.106 14.019 4.293 14.207 C 4.684 14.598 5.317 14.598 5.707 14.207 L 8 11.914 L 10.293 14.207 C 10.684 14.598 11.317 14.598 11.707 14.207 C 11.895 14.019 12 13.766 12 13.5 C 12 13.234 11.895 12.98 11.707 12.793 L 8.707 9.793 C 8.317 9.402 7.684 9.402 7.293 9.793 L 4.293 12.793 C 4.106 12.98 4 13.234 4 13.5 Z" fill="#2e3436"/>
</svg>
0707010000002A000081A4000000000000000000000001690B16EB000001E4000000000000000000000000000000000000005900000000SwayNotificationCenter-0.12.3/data/icons/scalable/actions/swaync-down-small-symbolic.svg<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 3.292969 6.707031 l 4 4 c 0.390625 0.390625 1.023437 0.390625 1.414062 0 l 4 -4 c 0.390625 -0.390625 0.390625 -1.023437 0 -1.414062 s -1.023437 -0.390625 -1.414062 0 l -3.292969 3.292969 l -3.292969 -3.292969 c -0.390625 -0.390625 -1.023437 -0.390625 -1.414062 0 s -0.390625 1.023437 0 1.414062 z m 0 0" fill="#222222" fill-rule="evenodd"/></svg>
0707010000002B000081A4000000000000000000000001690B16EB000001E1000000000000000000000000000000000000005700000000SwayNotificationCenter-0.12.3/data/icons/scalable/actions/swaync-up-small-symbolic.svg<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 7.292969 5.292969 l -4 4 c -0.390625 0.390625 -0.390625 1.023437 0 1.414062 s 1.023437 0.390625 1.414062 0 l 3.292969 -3.292969 l 3.292969 3.292969 c 0.390625 0.390625 1.023437 0.390625 1.414062 0 s 0.390625 -1.023437 0 -1.414062 l -4 -4 c -0.390625 -0.390625 -1.023437 -0.390625 -1.414062 0 z m 0 0" fill="#222222" fill-rule="evenodd"/></svg>
0707010000002C000081A4000000000000000000000001690B16EB00000514000000000000000000000000000000000000002F00000000SwayNotificationCenter-0.12.3/data/meson.buildinstall_data('org.erikreider.swaync.gschema.xml',
  install_dir: join_paths(get_option('datadir'), 'glib-2.0/schemas')
)

compile_schemas = find_program('glib-compile-schemas', required: false)
if compile_schemas.found()
  test('Validate schema file', compile_schemas,
    args: ['--strict', '--dry-run', meson.current_source_dir()]
  )
endif

# SCSS Dependency
sassc = find_program('sassc')
assert(sassc.found())

# SCSS Compilation
style_css = custom_target(
  'SCSS Compilation',
  build_by_default: true,
  build_always_stale: true,
  input : 'style/style.scss',
  output : 'style.css',
  install: true,
  install_dir: config_path,
  command : [
    sassc,
    '-t',
    'expanded',
    '@INPUT@',
    '@OUTPUT@'
  ],
)

message(style_css.full_path())

blueprints = custom_target('blueprints',
  input: files(run_command('find', '.', '-name', '*.blp').stdout().strip().split('\n')),
  output: '.',
  command: [
    find_program('blueprint-compiler', required: true),
    'batch-compile',
    '@OUTPUT@', '@CURRENT_SOURCE_DIR@', '@INPUT@'
  ],
  build_always_stale: true,
  build_by_default: true,
)

app_resources += gnome.compile_resources('sway_notification_center-resources',
  'sway_notification_center.gresource.xml',
    dependencies: [ blueprints ],
  c_name: 'sway_notification_center'
)
0707010000002D000081A4000000000000000000000001690B16EB00000173000000000000000000000000000000000000004500000000SwayNotificationCenter-0.12.3/data/org.erikreider.swaync.gschema.xml<?xml version="1.0" encoding="UTF-8"?>
<schemalist gettext-domain="swaync">
	<schema id="org.erikreider.swaync" path="/org/erikreider/swaync/">
		<key name="dnd-state" type="b">
			<default>false</default>
			<summary>The current do not disturb state</summary>
			<description>Whether notifications should be silent or not</description>
		</key>
	</schema>
</schemalist>
0707010000002E000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002900000000SwayNotificationCenter-0.12.3/data/style0707010000002F000081A4000000000000000000000001690B16EB000002AC000000000000000000000000000000000000004100000000SwayNotificationCenter-0.12.3/data/style/pre-gtk4-variables.scss/* Fallback for older CSS themes */
@define-color cc-bg rgba(46, 46, 46, 0.7);

@define-color noti-border-color rgba(255, 255, 255, 0.15);
@define-color noti-bg rgba(48, 48, 48, 0.8);
@define-color noti-bg-opaque rgb(48, 48, 48);
@define-color noti-bg-darker rgb(38, 38, 38);
@define-color noti-bg-hover rgb(56, 56, 56);
@define-color noti-bg-hover-opaque rgb(56, 56, 56);
@define-color noti-bg-focus rgba(68, 68, 68, 0.6);
@define-color noti-close-bg rgba(255, 255, 255, 0.1);
@define-color noti-close-bg-hover rgba(255, 255, 255, 0.15);

@define-color text-color rgb(255, 255, 255);
@define-color text-color-disabled rgb(150, 150, 150);

@define-color bg-selected rgb(0, 128, 255);
07070100000030000081A4000000000000000000000001690B16EB000025FA000000000000000000000000000000000000003400000000SwayNotificationCenter-0.12.3/data/style/style.scss:root {
  --cc-bg: rgba(46, 46, 46, 0.7);

  --noti-border-color: rgba(255, 255, 255, 0.15);
  --noti-bg: 48, 48, 48;
  --noti-bg-alpha: 0.8;
  --noti-bg-darker: rgb(38, 38, 38);
  --noti-bg-hover: rgb(56, 56, 56);
  --noti-bg-focus: rgba(68, 68, 68, 0.6);
  --noti-close-bg: rgb(78, 78, 78);
  --noti-close-bg-hover: rgb(94, 94, 94);

  --text-color: rgb(255, 255, 255);
  --text-color-disabled: rgb(150, 150, 150);

  --bg-selected: rgb(0, 128, 255);

  --notification-icon-size: 64px;
  --notification-app-icon-size: calc(var(--notification-icon-size) / 3);
  --notification-group-icon-size: 32px;

  --border: 1px solid var(--noti-border-color);
  --border-radius: 12px;

  --notification-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3),
    0 1px 3px 1px rgba(0, 0, 0, 0.7), 0 2px 6px 2px rgba(0, 0, 0, 0.3);
  --font-size-body: 15px;
  --font-size-summary: 16px;

  /* Deprecated variables (because of their typos). Keeeping them around for backwards compatibility. */
  --hover-tranistion: background 0.15s ease-in-out;
  --group-collapse-tranistion: opacity 400ms ease-in-out;

  --hover-transition: var(--hover-tranistion);
  --group-collapse-transition: var(--group-collapse-tranistion);
}

/* Fallback for older CSS themes */
@import "pre-gtk4-variables";

$margin: 8px;

notificationwindow, blankwindow, blankwindow {
  background: transparent;
}

.close-button {
  /* The notification Close Button */
  background: var(--noti-close-bg);
  color: var(--text-color);
  text-shadow: none;
  padding: 0;
  border-radius: 100%;
  margin-top: $margin;
  margin-right: $margin;
  box-shadow: none;
  border: none;
  min-width: 24px;
  min-height: 24px;

  &:hover {
    box-shadow: none;
    background: var(--noti-close-bg-hover);
    transition: var(--hover-tranistion);
    border: none;
  }
}

.notification-row {
  background: none;
  outline: none;

  &:focus {
    background: var(--noti-bg-focus);
  }

  .notification-background {
    padding: 6px 12px;

    .close-button {}

    .notification {
      /* The actual notification */
      border-radius: var(--border-radius);
      border: var(--border);
      padding: 0;
      transition: var(--hover-tranistion);
      background: rgba(var(--noti-bg), var(--noti-bg-alpha));

      &.low {
        /* Low Priority Notification */
      }

      &.normal {
        /* Normal Priority Notification */
      }

      &.critical {
        /* Critical Priority Notification */
      }

      .notification-default-action {
        /* The large action that also displays the notification summary and body */
        padding: $margin / 2;
        margin: 0;
        box-shadow: none;
        background: transparent;
        border: none;
        color: var(--text-color);
        transition: var(--hover-tranistion);
        border-radius: var(--border-radius);

        &:hover {
          -gtk-icon-filter: none;
          background: var(--noti-bg-hover);
        }

        &:not(:only-child) {
          /* When alternative actions are visible */
          border-bottom-left-radius: 0px;
          border-bottom-right-radius: 0px;
        }

        .notification-content {
          background: transparent;
          border-radius: var(--border-radius);
          $margin: 4px;
          padding: 0;

          .image {
            /* Notification Primary Image */
            -gtk-icon-filter: none;
            -gtk-icon-size: var(--notification-icon-size);
            border-radius: 100px;
            /* Size in px */
            margin: $margin;
          }

          .app-icon {
            /* Notification app icon (only visible when the primary image is set) */
            -gtk-icon-filter: none;
            -gtk-icon-size: var(--notification-app-icon-size);
            -gtk-icon-shadow: 0 1px 4px black;
            margin: 6px;
          }

          .text-box {
            label {
              /* Fixes base GTK 4 CSS setting a filter of opacity 50% for some odd reason */
              filter: none;
            }

            .summary {
              /* Notification summary/title */
              font-size: var(--font-size-summary);
              font-weight: bold;
              background: transparent;
              color: var(--text-color);
              text-shadow: none;
            }

            .time {
              /* Notification time-ago */
              font-size: var(--font-size-summary);
              font-weight: bold;
              background: transparent;
              color: var(--text-color);
              text-shadow: none;
              margin-right: 30px;
            }

            .body {
              /* Notification body */
              font-size: var(--font-size-body);
              font-weight: normal;
              background: transparent;
              color: var(--text-color);
              text-shadow: none;
            }
          }

          progressbar {
            /* The optional notification progress bar */
            margin-top: $margin;
          }

          .body-image {
            /* The "extra" optional bottom notification image */
            margin-top: $margin;
            background-color: white;
            -gtk-icon-filter: none;
          }

          .inline-reply {
            /* The inline reply section */
            margin-top: $margin;

            .inline-reply-entry {
              background: var(--noti-bg-darker);
              color: var(--text-color);
              caret-color: var(--text-color);
              border: var(--border);
              border-radius: var(--border-radius);
            }

            .inline-reply-button {
              margin-left: 4px;
              background: rgba(var(--noti-bg), var(--noti-bg-alpha));
              border: var(--border);
              border-radius: var(--border-radius);
              color: var(--text-color);

              &:disabled {
                background: initial;
                color: var(--text-color-disabled);
                border: var(--border);
                border-color: transparent;
              }

              &:hover {
                background: var(--noti-bg-hover);
              }
            }
          }
        }
      }

      .notification-alt-actions {
        background: none;
        border-bottom-left-radius: var(--border-radius);
        border-bottom-right-radius: var(--border-radius);
        padding: $margin / 2;
      }

      .notification-action {
        /* The alternative actions below the default action */
        margin: $margin / 2;
        padding: 0;

        &>button {
          border-radius: var(--border-radius);
          color: var(--text-color);
        }
      }
    }
  }
}

.notification-group {
  /* Styling only for Grouped Notifications */
  transition: opacity 200ms ease-in-out;

  &:focus {
    background: var(--noti-bg-focus);
  }

  &.low {
    /* Low Priority Group */
  }

  &.normal {
    /* Low Priority Group */
  }

  &.critical {
    /* Low Priority Group */
  }

  /* The groups close button */
  .notification-group-close-button .close-button {
    margin: $margin * 1.5 $margin * 2.5;
  }

  %header {
    margin: 0 $margin * 2;
    color: var(--text-color);
  }

  .notification-group-headers {
    /* Notification Group Headers */
    @extend %header;

    .notification-group-icon {
      color: var(--text-color);
      -gtk-icon-size: var(--notification-group-icon-size);
    }

    .notification-group-header {
      color: var(--text-color);
    }
  }

  .notification-group-buttons {
    /* Notification Group Buttons */
    @extend %header;
  }

  &.collapsed {

    /* When another group is expanded, lower the opacity of the collapsed ones */
    &.not-expanded {
      opacity: 0.4;
    }

    .notification-row {
      .notification {
        background-color: rgba(var(--noti-bg), 1);
      }

      &:not(:last-child) {

        /* Top notification in stack */
        /* Set lower stacked notifications opacity to 0 */
        .notification-action,
        .notification-default-action {
          opacity: 0;
        }
      }
    }

    &:hover {
      .notification-row:not(:only-child) {
        .notification {
          background-color: var(--noti-bg-hover);
        }
      }
    }
  }
}

.control-center {
  /* The Control Center which contains the old notifications + widgets */
  background: var(--cc-bg);
  color: var(--text-color);
  border-radius: var(--border-radius);

  .control-center-list-placeholder {
    /* The placeholder when there are no notifications */
    opacity: 0.5;
  }

  .control-center-list {
    /* List of notifications */
    background: transparent;

    .notification {
      box-shadow: var(--notification-shadow);

      .notification-default-action,
      .notification-action {
        transition: var(--group-collapse-tranistion), var(--hover-tranistion);

        &:hover {
          background-color: var(--noti-bg-hover);
        }
      }
    }
  }
}

.blank-window {
  /* Window behind control center and on all other monitors */
  background: transparent;
}

.floating-notifications {
  background: transparent;

  .notification {
    box-shadow: none;
  }
}

/*** Widgets ***/
.widget {
  margin: $margin;
  padding: $margin;
  border-radius: var(--border-radius);
}

/* Title widget */
@import "widgets/title";
/* DND widget */
@import "widgets/dnd";
/* Label widget */
@import "widgets/label";
/* Mpris widget */
@import "widgets/mpris";
/* Buttons widget */
@import "widgets/buttons";
/* Menubar widget */
@import "widgets/menubar";
/* Volume widget */
@import "widgets/volume";
/* Slider widget */
@import "widgets/slider";
/* Backlight widget */
@import "widgets/backlight";
/* Inhibitors widget */
@import "widgets/inhibitors";
07070100000031000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000003100000000SwayNotificationCenter-0.12.3/data/style/widgets07070100000032000081A4000000000000000000000001690B16EB00000016000000000000000000000000000000000000004000000000SwayNotificationCenter-0.12.3/data/style/widgets/backlight.scss.widget-backlight {
}
07070100000033000081A4000000000000000000000001690B16EB000000C2000000000000000000000000000000000000003E00000000SwayNotificationCenter-0.12.3/data/style/widgets/buttons.scss.widget-buttons-grid {
  & flowboxchild>button {
    border-radius: var(--border-radius);

    &.toggle:checked {
      /* style given to the active toggle button */
    }

    &:hover {}
  }
}
07070100000034000081A4000000000000000000000001690B16EB00000112000000000000000000000000000000000000003A00000000SwayNotificationCenter-0.12.3/data/style/widgets/dnd.scss.widget-dnd {
  label {
    color: var(--text-color);
    margin-right: $margin;
    font-size: 1.1rem;
  }

  switch {
    border-radius: var(--border-radius);
    margin-left: $margin;

    &:checked {}

    slider {
      border-radius: var(--border-radius);
    }
  }
}
07070100000035000081A4000000000000000000000001690B16EB000000B3000000000000000000000000000000000000004100000000SwayNotificationCenter-0.12.3/data/style/widgets/inhibitors.scss.widget-inhibitors {
  & > label {
    margin-right: $margin;
    font-size: 1.5rem;
  }

  & > button {
    margin-left: $margin;
    border-radius: var(--border-radius);
  }
}

07070100000036000081A4000000000000000000000001690B16EB0000003B000000000000000000000000000000000000003C00000000SwayNotificationCenter-0.12.3/data/style/widgets/label.scss.widget-label {
  & > label {
    font-size: 1.1rem;
  }
}
07070100000037000081A4000000000000000000000001690B16EB000003E3000000000000000000000000000000000000003E00000000SwayNotificationCenter-0.12.3/data/style/widgets/menubar.scss.widget-menubar {
  & > .menu-button-bar {
    /* The left button container */
    & > .start {
      margin-left: $margin;
    }

    /* The right button container */
    & > .end {
      margin-right: $margin;
    }

    /* The left and right button container */
    & > .widget-menubar-container {
      button {
        border-radius: var(--border-radius);
        margin: 0 $margin / 2;
      }
    }
  }

  /* The revealer buttons */
  & > revealer {
    * {
      margin-top: $margin;

      button {
        border-radius: var(--border-radius);
        margin: $margin;
        margin-top: 0;

        &:last-child {
          margin-bottom: 0;
        }
      }
    }
  }

  /* .AnyName { Name defined in config after #
    background-color: rgba(var(--noti-bg), 1.0);
    padding: 8px;
    margin: 8px;
    border-radius: 12px;
  }

  .AnyName>button {
    background: transparent;
    border: none;
  }

  .AnyName>button:hover {
    background-color: var(--noti-bg-hover);
  } */
}

07070100000038000081A4000000000000000000000001690B16EB00000553000000000000000000000000000000000000003C00000000SwayNotificationCenter-0.12.3/data/style/widgets/mpris.scss:root {
  --mpris-album-art-overlay: rgba(0, 0, 0, 0.55);
  --mpris-button-hover: rgba(0, 0, 0, 0.5);
  --mpris-album-art-icon-size: 96px;
  --mpris-album-art-shadow: 0px 0px 10px rgba(0, 0, 0, 0.75);
}

.widget-mpris {
  padding: 0;

  /* The parent to all players */
  .widget-mpris-player {
    margin: 16px 20px;
    border-radius: var(--border-radius);
    box-shadow: var(--mpris-album-art-shadow);

    .mpris-background {
      filter: blur(10px);
    }

    .mpris-overlay {
      padding: 16px;
      background-color: var(--mpris-album-art-overlay);

      button:hover {
        /* The media player buttons (play, pause, next, etc...) */
        background: var(--noti-bg-hover);
      }

      .widget-mpris-album-art {
        border-radius: var(--border-radius);
        box-shadow: var(--mpris-album-art-shadow);
        -gtk-icon-size: var(--mpris-album-art-icon-size);
      }

      .widget-mpris-title {
        font-weight: bold;
        font-size: 1.25rem;
      }

      .widget-mpris-subtitle {
        font-size: 1.1rem;
      }

      &>box>button {

        /* Change player control buttons */
        &:hover {
          background-color: var(--mpris-button-hover);
        }
      }
    }
  }

  &>box>button {
    /* Change player side buttons */
  }

  &>box>button:disabled {
    /* Change player side buttons insensitive */
  }
}
07070100000039000081A4000000000000000000000001690B16EB00000039000000000000000000000000000000000000003D00000000SwayNotificationCenter-0.12.3/data/style/widgets/slider.scss.widget-slider {
  label {
    font-size: inherit;
  }
}
0707010000003A000081A4000000000000000000000001690B16EB000000AE000000000000000000000000000000000000003C00000000SwayNotificationCenter-0.12.3/data/style/widgets/title.scss.widget-title {
  & > label {
    margin-right: $margin;
    font-size: 1.5rem;
  }

  & > button {
    margin-left: $margin;
    border-radius: var(--border-radius);
  }
}

0707010000003B000081A4000000000000000000000001690B16EB0000015A000000000000000000000000000000000000003D00000000SwayNotificationCenter-0.12.3/data/style/widgets/volume.scss:root {
  --widget-volume-row-icon-size: 24px;
}

.widget-volume {
}

.widget-volume>box>button {}

/* Each row app icon */
.widget-volume row image {
  -gtk-icon-size: var(--widget-volume-row-icon-size);
}

.per-app-volume {
  background-color: var(--noti-bg-alt);
  margin: $margin;
  margin-bottom: 0;
  border-radius: var(--border-radius);
}
0707010000003C000081A4000000000000000000000001690B16EB0000035D000000000000000000000000000000000000004A00000000SwayNotificationCenter-0.12.3/data/sway_notification_center.gresource.xml<?xml version="1.0" encoding="UTF-8"?>
<gresources>
  <gresource prefix="/org/erikreider/swaync">
    <file preprocess="xml-stripblanks">icons/scalable/actions/swaync-collapse-symbolic.svg</file>
    <file preprocess="xml-stripblanks">icons/scalable/actions/swaync-close-symbolic.svg</file>
    <file preprocess="xml-stripblanks">icons/scalable/actions/swaync-up-small-symbolic.svg</file>
    <file preprocess="xml-stripblanks">icons/scalable/actions/swaync-down-small-symbolic.svg</file>

    <file preprocess="xml-stripblanks">ui/notification_window.ui</file>
    <file preprocess="xml-stripblanks">ui/notification.ui</file>
    <file preprocess="xml-stripblanks">ui/control_center.ui</file>
    <file preprocess="xml-stripblanks">ui/mpris_player.ui</file>
    <file preprocess="xml-stripblanks">ui/notifications_widget.ui</file>
  </gresource>
</gresources>
0707010000003D000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002600000000SwayNotificationCenter-0.12.3/data/ui0707010000003E000081A4000000000000000000000001690B16EB000001C3000000000000000000000000000000000000003900000000SwayNotificationCenter-0.12.3/data/ui/control_center.blpusing Gtk 4.0;

template $SwayNotificationCenterControlCenter: ApplicationWindow {
  name: "control-center-window";

  styles [
    "blank-window",
  ]

  ScrolledWindow window {
    hscrollbar-policy: never;
    overflow: hidden;

    styles [
      "control-center",
    ]

    child: Viewport {
      child: $IterBox box {
        orientation: vertical;
        vexpand: true;

        styles [
          "widgets",
        ]
      };
    };
  }
}
0707010000003F000081A4000000000000000000000001690B16EB0000090B000000000000000000000000000000000000003700000000SwayNotificationCenter-0.12.3/data/ui/mpris_player.blpusing Gtk 4.0;

template $SwayNotificationCenterWidgetsMprisMprisPlayer: $Underlay {
  overflow: hidden;
  valign: fill;
  hexpand: true;

  underlay_child: Picture background_picture {
    content-fit: cover;

    styles [
      "mpris-background",
    ]
  };

  child: Box {
    orientation: vertical;

    styles [
      "mpris-overlay",
    ]

    Box {
      vexpand: true;
      spacing: 12;

      Image album_art {
        icon-name: "audio-x-generic-symbolic";
        use-fallback: true;
        pixel-size: 96;
        overflow: hidden;

        styles [
          "widget-mpris-album-art",
        ]
      }

      Box {
        orientation: vertical;
        spacing: 4;
        hexpand: true;
        valign: center;

        Label title {
          justify: left;
          ellipsize: end;
          wrap-mode: word_char;
          xalign: 0;
          yalign: 0;

          styles [
            "widget-mpris-title",
          ]
        }

        Label sub_title {
          justify: left;
          ellipsize: end;
          wrap-mode: word_char;
          xalign: 0;
          yalign: 0;

          styles [
            "widget-mpris-subtitle",
          ]
        }
      }
    }

    Box {
      spacing: 6;
      halign: center;

      Button button_shuffle {
        has-frame: false;
        icon-name: "media-playlist-shuffle-symbolic";

        styles [
          "circular",
          "image-button",
          "flat",
        ]
      }

      Button button_prev {
        has-frame: false;
        icon-name: "media-seek-backward-symbolic";

        styles [
          "circular",
          "image-button",
          "flat",
        ]
      }

      Button button_play_pause {
        has-frame: false;
        icon-name: "media-playback-pause-symbolic";

        styles [
          "circular",
          "image-button",
          "flat",
        ]
      }

      Button button_next {
        has-frame: false;
        icon-name: "media-seek-forward-symbolic";

        styles [
          "circular",
          "image-button",
          "flat",
        ]
      }

      Button button_repeat {
        has-frame: false;
        icon-name: "media-playlist-repeat-symbolic";

        styles [
          "circular",
          "image-button",
          "flat",
        ]
      }
    }
  };
}
07070100000040000081A4000000000000000000000001690B16EB000010AF000000000000000000000000000000000000003700000000SwayNotificationCenter-0.12.3/data/ui/notification.blpusing Gtk 4.0;
using Adw 1;

template $SwayNotificationCenterNotification: Adw.Bin {
  hexpand: true;

  styles [
    "notification-row",
  ]

  Revealer revealer {
    transition-type: crossfade;
    reveal-child: false;
    hexpand: true;

    child: $DismissibleWidget dismissible_widget {
      halign: fill;
      valign: fill;

      child: Overlay base_widget {
        hexpand: true;

        styles [
          "notification-background",
        ]

        $IterBox base_box {
          orientation: vertical;
          overflow: hidden;

          styles [
            "notification",
          ]

          Box default_action {
            orientation: vertical;

            styles [
              "notification-default-action",
            ]

            Box {
              orientation: vertical;
              spacing: 8;

              styles [
                "notification-content",
              ]

              Box {
                // Box with images/icons
                Overlay {
                  halign: center;
                  valign: center;

                  child: Image img {
                    valign: center;
                    overflow: hidden;

                    styles [
                      "image",
                    ]
                  };

                  [overlay]
                  Image img_app_icon {
                    halign: end;
                    valign: end;
                    overflow: hidden;

                    styles [
                      "app-icon",
                    ]
                  }
                }

                // Body, summary, and time
                Box {
                  hexpand: true;
                  vexpand: true;
                  valign: center;
                  orientation: vertical;

                  styles [
                    "text-box",
                  ]

                  Box {
                    orientation: horizontal;
                    valign: center;

                    Label summary {
                      justify: left;
                      ellipsize: end;
                      wrap: false;
                      xalign: 0;
                      hexpand: true;

                      styles [
                        "summary",
                      ]
                    }

                    Label time {
                      justify: left;
                      ellipsize: none;
                      valign: start;

                      styles [
                        "time",
                      ]
                    }
                  }

                  Label body {
                    ellipsize: end;
                    justify: left;
                    wrap-mode: word_char;
                    natural-wrap-mode: word;
                    lines: 1;
                    xalign: 0;
                    yalign: 0;
                    hexpand: true;

                    styles [
                      "body",
                    ]
                  }
                }
              }

              ProgressBar progress_bar {
                orientation: horizontal;
              }

              Picture body_image {
                halign: center;
                overflow: hidden;
              }

              Box inline_reply_box {
                styles [
                  "inline-reply",
                ]

                Entry inline_reply_entry {
                  input-purpose: free_form;
                  input-hints: spellcheck | emoji;
                  hexpand: true;

                  styles [
                    "inline-reply-entry",
                  ]
                }

                Button inline_reply_button {
                  styles [
                    "inline-reply-button",
                  ]
                }
              }
            }
          }

          FlowBox alt_actions_box {
            orientation: horizontal;
            homogeneous: true;
            overflow: hidden;
            selection-mode: none;
            activate-on-single-click: true;

            styles [
              "notification-alt-actions",
            ]
          }
        }

        [overlay]
        $SwayNotificationCenterNotificationCloseButton close_button {}
      };
    };
  }
}
07070100000041000081A4000000000000000000000001690B16EB000001A6000000000000000000000000000000000000003E00000000SwayNotificationCenter-0.12.3/data/ui/notification_window.blpusing Gtk 4.0;

template $SwayNotificationCenterNotificationWindow: Gtk.ApplicationWindow {
  styles [
    "floating-notifications",
  ]

  Gtk.ScrolledWindow scrolled_window {
    propagate-natural-height: true;
    hscrollbar-policy: never;
    vscrollbar-policy: automatic;
    has-frame: false;

    $AnimatedList list {
      scroll_to_append: true;
      transition_children: true;

      vexpand: true;
    }
  }
}
07070100000042000081A4000000000000000000000001690B16EB000004DE000000000000000000000000000000000000003F00000000SwayNotificationCenter-0.12.3/data/ui/notifications_widget.blpusing Gtk 4.0;

template $SwayNotificationCenterWidgetsNotifications: $SwayNotificationCenterWidgetsBaseWidget {
  orientation: vertical;
  overflow: hidden;

  Stack stack {
    hhomogeneous: true;
    vhomogeneous: true;
    transition-type: crossfade;
    transition-duration: 200;
    vexpand: true;

    StackPage {
      name: "notifications-list";

      child: ScrolledWindow scrolled_window {
        hscrollbar-policy: never;
        has-frame: false;

        child: Viewport viewport {
          vexpand: true;

          child: ListBox list_box {
            valign: fill;

            styles [
              "control-center-list",
            ]
          };
        };
      };
    }

    StackPage {
      name: "notifications-placeholder";

      child: Box {
        halign: center;
        valign: center;
        hexpand: true;
        vexpand: true;
        orientation: vertical;
        spacing: 12;

        styles [
          "control-center-list-placeholder",
        ]

        Image {
          pixel-size: 96;
          icon-name: "preferences-system-notifications-symbolic";
          use-fallback: true;
        }

        Label text_empty_label {
          label: "No Notifications";
        }
      };
    }
  }
}
07070100000043000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002200000000SwayNotificationCenter-0.12.3/man07070100000044000081A4000000000000000000000001690B16EB000006F8000000000000000000000000000000000000003600000000SwayNotificationCenter-0.12.3/man/swaync-client.1.scdswaync-client(1)

# NAME

swaync-client - Client executable

# USAGE

*swaync-client* [option]

# OPTIONS

*-h, --help*
	Show help options

*-v, --version*
	Prints version

*-R, --reload-config*
	Reload the config file

*-rs, --reload-css*
	Reload the css file. Location change requires restart

*-t, --toggle-panel*
	Toggle the notification panel

*-op, --open-panel*
	Opens the notification panel

*-cp, --close-panel*
	Closes the notification panel

*-d, --toggle-dnd*
	Toggle and print the current dnd state

*-D, --get-dnd*
	Print the current dnd state

*-dn, --dnd-on*
	Turn dnd on and print the new dnd state

*-df, --dnd-off*
	Turn dnd off and print the new dnd state

*-I, --get-inhibited*
	Print if currently inhibited or not

*-In, --get-num-inhibitors*
	Print number of inhibitors

*-Ia, --inhibitor-add [APP_ID]*
	Add an inhibitor

*-Ir, --inhibitor-remove [APP_ID]*
	Remove an inhibitor

*-Ic, --inhibitors-clear*
	Clears all inhibitors

*-c, --count*
	Print the current notification count

*--hide-latest*
	Hides latest notification. Still shown in Control Center

*--hide-all*
	Hides all notifications. Still shown in Control Center

*--close-latest*
	Closes latest notification

*-C, --close-all*
	Closes all notifications

*-a, --action [ACTION_INDEX]*
	Invokes the action [ACTION_INDEX] (or 0) of the latest notification

*-sw, --skip-wait*
	Doesn't wait when swaync hasn't been started

*-s, --subscribe*
	Subscribe to notification add and close events

*-swb, --subscribe-waybar*
	Subscribe to notification add and close events with waybar support. Read README for example

*--change-cc-monitor*
	Changes the preferred control center monitor (resets on config reload)

*--change-noti-monitor*
	Changes the preferred notification monitor (resets on config reload)
07070100000045000081A4000000000000000000000001690B16EB00000E36000000000000000000000000000000000000002F00000000SwayNotificationCenter-0.12.3/man/swaync.1.scdswaync(1)

# NAME

swaync - A simple notification daemon with a GTK gui for notifications and the control center

# USAGE

*swaync* [option]

# OPTIONS

*-h, --help*
	Show help options

*-v, --version*
	Prints version

*-s, --style* <CSS file>
	Use a custom Stylesheet file

*--skip-system-css*
	Skip trying to parse the packaged Stylesheet file. Useful for CSS debugging

*-c, --config* <Config file>
	Use a custom config file

# Control Center Keyboard Shortcuts

*Up/Down*: Navigate notifications

*Home*: Navigate to the latest notification

*End*: Navigate to the oldest notification

*Escape/Caps_Lock*: Close notification panel

*Return*: Execute default action or close notification if none

*Delete/BackSpace*: Close notification

*Shift+C*: Close all notifications

*Shift+D*: Toggle Do Not Disturb

*Buttons 1-9*: Execute alternative actions

*Left click button / actions*: Activate notification action

*Middle/Right click notification*: Close notification

# CONFIGURATION

The main config file is located in */etc/xdg/swaync/config.json*. Copy it over
to your *~/.config/swaync/* folder to customize without needing root access.

To reload the config, you'll need to run *swaync-client --reload-config*

For information on the config file format, see *swaync*(5)


# Scripting

Scripting rules and logic:

. *Only one* script can be fired per notification
. Each script requires `exec` and at least one of the other properties
. All listed properties must match the notification for the script to be ran
. If any of the properties doesn't match, the script will be skipped
. If a notification doesn't include one of the properties, that property will be skipped

Notification information can be printed into a terminal by running
*G_MESSAGES_DEBUG=all swaync* (when a notification appears).

Config properties:

```
{
	"scripts": {
		"example-script": {
			"exec": "Your shell command or script here...",
			"app-name": "Notification app-name Regex",
			"summary": "Notification summary Regex",
			"body": "Notification body Regex",
			"urgency": "Low or Normal or Critical",
			"category": "Notification category Regex"
		}
	}
	other non scripting properties...
}
```

*config.json* example:

```
{
	"scripts": {
		// This script will only run when Spotify sends a notification containing
		// that exact summary and body
		"example-script": {
			"exec": "~/.config/swaync/myRickRollScript.sh",
			"app-name": "Spotify"
			"summary": "Never Gonna Give You Up",
			"body": "Rick Astley - Whenever You Need Somebody"
		}
	}
	other non scripting properties...
}
```

## Disable scripting

To completely disable scripting, the project needs to be built like so:

```
meson build -Dscripting=false
ninja -C build
meson install -C build
```

# Waybar Example

This example requires *NotoSansMono Nerd Font* to get the icons looking right

Waybar config

```
"custom/notification": {
	"tooltip": false,
	"format": "{icon}",
	"format-icons": {
		"notification": "ï‚¢<span foreground='red'><sup>ï‘„</sup></span>",
		"none": "ï‚¢",
		"dnd-notification": "<span foreground='red'><sup></sup></span>",
		"dnd-none": "",
		"inhibited-notification": "ï‚¢<span foreground='red'><sup>ï‘„</sup></span>",
		"inhibited-none": "ï‚¢",
		"dnd-inhibited-notification": "<span foreground='red'><sup></sup></span>",
		"dnd-inhibited-none": ""
	},
	"return-type": "json",
	"exec-if": "which swaync-client",
	"exec": "swaync-client -swb",
	"on-click": "swaync-client -t -sw",
	"on-click-right": "swaync-client -d -sw",
	"escape": true
},
```

Waybar css file

```
#custom-notification {
	font-family: "NotoSansMono Nerd Font";
}
```
07070100000046000081A4000000000000000000000001690B16EB00006885000000000000000000000000000000000000002F00000000SwayNotificationCenter-0.12.3/man/swaync.5.scdswaync(5)

# NAME

swaync - Configuration file

# CONFIGURATION

Using a text editor with a JSON language server is recommended when editing the
config file to be able to detect config errors

*ignore-gtk-theme* ++
	type: bool ++
	default: true ++
	description: Unsets the GTK_THEME environment variable, fixing a lot of ++
				 issues with GTK themes ruining the users custom CSS themes.

*positionX* ++
	type: string ++
	default: right ++
	values: left, right, center ++
	description: Horizontal position of control center and notification window

*positionY* ++
	type: string ++
	default: top ++
	values: top, center, bottom ++
	description: Vertical position of control center and notification window

*layer* ++
	type: string ++
	default: overlay ++
	values: background, bottom, top, overlay ++
	description: Layer of notification window relative to normal windows. ++
		background is below all windows, overlay is above all windows.

*layer-shell* ++
	type: bool ++
	default: true ++
	description: Wether or not the windows should be opened as layer-shell ++
		surfaces. Note: Requires swaync restart to apply

*cssPriority* ++
	type: string ++
	default: application ++
	values: application, user ++
	description: Which GTK priority to use when loading the default and user ++
		CSS files. Pick "user" to override *XDG_CONFIG_HOME/gtk-3.0/gtk.css*

*control-center-positionX* ++
	type: string ++
	default: right ++
	values: left, right, center ++
	description: Optional: Horizontal position of the control center. ++
		Supersedes positionX if not set to `none`.

*control-center-positionY* ++
	type: string ++
	default: top ++
	values: top, center, bottom ++
	description: Optional: Vertical position of the control center. ++
		Supersedes positionY if not set to `none`.

*control-center-margin-top* ++
	type: integer ++
	default: 0 ++
	description: The margin (in pixels) at the top of the notification center. 0 to disable

*control-center-margin-bottom* ++
	type: integer ++
	default: 0 ++
	description: The margin (in pixels) at the bottom of the notification center. 0 to disable

*control-center-margin-right* ++
	type: integer ++
	default: 0 ++
	description: The margin (in pixels) at the right of the notification center. 0 to disable

*control-center-margin-left* ++
	type: integer ++
	default: 0 ++
	description: The margin (in pixels) at the left of the notification center. 0 to disable

*control-center-layer* ++
	type: string ++
	default: top ++
	values: background, bottom, top, overlay ++
	description: Layer of control center window relative to normal windows. ++
		background is below all windows, overlay is above all windows.

*control-center-exclusive-zone* ++
	type: bool ++
	default: true ++
	description: Whether or not the control center should follow the ++
		compositors exclusive zones. An example would be setting it to ++
		*false* to cover your panel/dock.

*notification-2fa-action* ++
	type: bool ++
	default: true ++
	description: If each notification should display a 'COPY \"1234\"' action

*timeout* ++
	type: integer ++
	default: 10 ++
	description: The notification timeout for notifications with normal priority

*timeout-low* ++
	type: integer ++
	default: 5 ++
	description: The notification timeout for notifications with low priority

*timeout-critical* ++
	type: integer ++
	default: 0 ++
	description: The notification timeout for notifications with critical priority. 0 to disable

*notification-window-width* ++
	type: integer ++
	default: 500 ++
	description: Width of the notification in pixels

*notification-window-height* ++
	type: integer ++
	default: -1 ++
	description: Max height of the notification in pixels. -1 to use ++
				 the full amount of space given by the compositor.

*notification-window-preferred-output* ++
	type: string ++
	default: "" ++
	description: The preferred output to open the notification window ++
				 (popup notifications). Can either be the monitor connector ++
				 name (ex: "DP-1"), or the full name, manufacturer model serial ++
				 (ex: "Acer Technologies XV272U V 503023B314202"). ++
				 If the output is not found, the currently focused one is picked.

*keyboard-shortcuts* ++
	type: bool ++
	default: true ++
	description: If control center should use keyboard shortcuts

*notification-grouping* ++
	type: bool ++
	default: true ++
	description: If notifications should be grouped by app name

*image-visibility* ++
	type: string ++
	default: always ++
	values: always, when-available, never ++
	description: An explanation about the purpose of this instance.

*transition-time* ++
	type: integer ++
	default: 200 ++
	description: The notification animation duration. 0 to disable

*hide-on-clear* ++
	type: bool ++
	default: false ++
	description: Hides the control center after pressing "Clear All"

*hide-on-action* ++
	type: bool ++
	default: true ++
	description: Hides the control center when clicking on notification action

*text-empty* ++
	type: string ++
	default: "No Notifications" ++
	description: Text that appears when there are no notifications to show

*fit-to-screen* ++
	type: bool ++
	default: true ++
	description: Whether the control center should expand vertically to fill the screen

*relative-timestamps* ++
	type: bool ++
	default: true ++
	description: Display notification timestamps relative to now e.g. \"26 minutes ago\". ++
		If false, a local iso8601-formatted absolute timestamp is displayed.

*control-center-height* ++
	type: integer ++
	default: 500 ++
	description: Height of the control center in pixels. ++
		A value of -1 means that it will fit to the content. ++
		Ignored when 'fit-to-screen' is set to 'true'. ++
		Also limited to the height of the monitor, unless ++
		'layer-shell-cover-screen' is set to false.

*control-center-width* ++
	type: integer ++
	default: 500 ++
	description: The control center width in pixels

*control-center-preferred-output* ++
	type: string ++
	default: "" ++
	description: The preferred output to open the control center. Can either ++
				 be the monitor connector name (ex: "DP-1"), or ++
				 the full name, manufacturer model serial ++
				 (ex: "Acer Technologies XV272U V 503023B314202"). If the ++
				 output is not found, the currently focused one is picked.

*notification-action-filter* ++
	type: object ++
	visibility object properties: ++
		*use-regex*++
			type: bool ++
			optional: true ++
			default: false ++
			description: Indicates if all the below fields should use ++
				regex or not.++
		*app-name*++
			type: string ++
			optional: true ++
			description: The app-name.++
		*desktop-entry*++
			type: string ++
			optional: true ++
			description: The desktop-entry.++
		*id-matcher*++
			type: string ++
			optional: true (needs at least one *matcher*) ++
			description: Matches the actions identifier. Can be found by ++
				reading the output of swaync when run with the ++
				*G_MESSAGES_DEBUG=all* environment variable.++
		*text-matcher*++
			type: string ++
			optional: true (needs at least one *matcher*) ++
			description: Matches the actions text visible in the notification. ++
	description: Hides matching action(s) of matching notifications. ++
		If the notification doesn't include one of the properties, that ++
		property will be ignored. If all properties match the given ++
		notification, the matching actions will be hidden. ++
	example:
```
{
	"notification-action-filter": {
		"hide-chromium-settings": {
			"desktop-entry": "chromium-browser",
			"use-regex": false,
			"id-matcher": "settings",
			"text-matcher": "Settings"
		}
	},
}
```

*notification-visibility* ++
	type: object ++
	visibility object properties: ++
		*state*++
			type: string ++
			optional: false ++
			default: enabled ++
			values: ignored, muted, transient, enabled ++
			description: The notification visibility state. ++
		*override-urgency*++
			type: string ++
			optional: true ++
			default: unset ++
			values: unset, low, normal, critical ++
			description: The new urgency for the notification if set.++
		*app-name*++
			type: string ++
			optional: true ++
			description: The app-name. Uses Regex.++
		*desktop-entry*++
			type: string ++
			optional: true ++
			description: The desktop-entry. Uses Regex.++
		*summary*++
			type: string ++
			optional: true ++
			description: The summary of the notification. Uses Regex.++
		*body*++
			type: string ++
			optional: true ++
			description: The body of the notification. Uses Regex.++
		*urgency*++
			type: string ++
			optional: true ++
			default: Normal ++
			values: Low, Normal, Critical ++
			description: The urgency of the notification.++
		*category*++
			type: string ++
			optional: true ++
			description: Which category the notification belongs to. Uses Regex.++
	description: Set the visibility or override urgency of each incoming ++
		notification. ++
		If the notification doesn't include one of the properties, that ++
		property will be ignored. All properties (except for state) use ++
		regex. If all properties match the given notification, the ++
		notification will be follow the provided state. ++
		Only the first matching object will be used. ++
	example:
```
{
	"notification-visibility": {
		"example-name": {
			"state": "The notification state",
			"app-name": "Notification app-name Regex",
			"summary": "Notification summary Regex",
			"body": "Notification body Regex",
			"urgency": "Low or Normal or Critical",
			"category": "Notification category Regex"
		}
	}
}
```

*widgets* ++
	type: array ++
	Default values: ["title", "dnd", "notifications"] ++
	Valid array values (see *widget-config* for more information): ++
		*notifications*++
			required: true ++
			optional: false ++
		*title*++
			optional: true ++
		*dnd*++
			optional: true ++
		*label*++
			optional: true ++
		*mpris*++
			optional: true ++
		*menubar*++
			optional: true ++
		*buttons-grid*++
			optional: true ++
		*slider*++
			optional: true ++
#START pulse-audio
		*volume*++
			optional: true ++
#END pulse-audio
		*backlight*++
			optional: true ++
		*inhibitors*++
			optional: true ++
	description: ++
		Which order and which widgets to display. ++
		If the \"notifications\" widget isn't specified, it ++
		will be placed at the bottom. ++
	multiple of same widget: ++
		Append a # with any value to the end of the widget name. ++
		Example: "title#TheMainTitle" ++
		To address this widget specifically in the css file use the css class .TheMainTitle ++
	example:
```
{
	"widgets": [
		"inhibitors",
		"title",
		"dnd",
		"notifications"
	]
}
```

*widget-config* ++
	type: object ++
	description: Configure specific widget properties. ++
	multiple of same widget: ++
		Append a # with any value to the end of the widget name. ++
		Example: "title#TheMainTitle" ++
		To address this widget specifically in the css file use the css class .TheMainTitle ++
	Widgets to customize: ++
		*notifications*++
			type: object ++
			css class: widget-notifications ++
			properties: ++
				vexpand: ++
					type: bool ++
					optional: true ++
					default: true ++
					description: Whether or not the notifications widget ++
								 should vertically expand or not ++
			description: The Notifications Widget. ++
		*title*++
			type: object ++
			css class: widget-title ++
			properties: ++
				text: ++
					type: string ++
					optional: true ++
					default: "Notifications" ++
					description: The title of the widget ++
				clear-all-button: ++
					type: bool ++
					optional: true ++
					default: true ++
					description: Whether to display a "Clear All" button ++
				button-text: ++
					type: string ++
					optional: true ++
					default: "Clear All" ++
					description: "Clear All" button text ++
			description: The notification visibility state. ++
		*dnd*++
			type: object ++
			css class: widget-dnd ++
			properties: ++
				text: ++
					type: string ++
					optional: true ++
					default: "Do Not Disturb" ++
					description: The title of the widget ++
			description: Control Center Do Not Disturb Widget. ++
		*label*++
			type: object ++
			css class: widget-label ++
			properties: ++
				text: ++
					type: string ++
					optional: true ++
					default: "Label Text" ++
					description: The text content of the widget ++
				clear-all-button: ++
					type: integer ++
					optional: true ++
					default: 5 ++
					description: The maximum lines ++
			description: A generic widget that allows the user to add custom text. ++
		*mpris*++
			type: object ++
			css classes: ++
				widget-mpris ++
				widget-mpris-player ++
				widget-mpris-title ++
				widget-mpris-subtitle ++
			properties: ++
				blacklist: ++
					type: array ++
					optional: true ++
					default: [] ++
					description: Audio sources for the mpris widget to ignore. ++
					Valid array values: ++
						type: string ++
						description: Audio source/app name. Regex allowed. Hint ++
							`$ qdbus | grep mpris` to find source names. ++
				autohide: ++
					type: bool ++
					optional: true ++
					default: false ++
					description: Whether to hide the widget when the ++
								 player has no metadata. ++
				show-album-art: ++
					type: string ++
					optional: true ++
					default: "always" ++
					description: Whether or not the album art should be ++
								 hidden, always visible, or only visible ++
								 when a valid album art is provided. ++
					enum: ["right", "left"] ++
				loop-carousel: ++
					type: bool ++
					optional: true ++
					default: false ++
					description: Whether to loop through the mpris carousel. ++
			description: A widget that displays multiple music players. ++
		*menubar*++
			type: object ++
			css classes: ++
				widget-menubar ++
				.widget-menubar>box>.menu-button-bar ++
				name of element given after menu or buttons with # ++
			patternProperties: ++
				menu#<name>: ++
					type: object ++
					properties: ++
						label: ++
							type: string ++
							optional: true ++
							default: "Menu" ++
							description: Label of button to show/hide menu dropdown ++
						position: ++
							type: string ++
							optional: true ++
							default: "right" ++
							description: Horizontal position of the button in the bar ++
							enum: ["right", "left"] ++
						animation-type: ++
							type: string ++
							optional: true ++
							default: "slide_down" ++
							description: Animation type for menu++
							enum: ["slide_down", "slide_up", "none"] ++
						animation-duration: ++
							type: integer ++
							optional: true ++
							default: 250 ++
							description: Duration of animation in milliseconds ++
						actions: ++
							type: array ++
							Default values: [] ++
							Valid array values: ++
								type: object ++
								properties: ++
									label: ++
										type: string ++
										default: "label" ++
										description: Text to be displayed in button ++
									command: ++
										type: string ++
										default: "" ++
										description: "Command to be executed on click" ++
									type: ++
										type: string ++
										default: "normal" ++
										description: Type of the button. ++
											Toggle buttons receive the '.active' css class ++
										enum: ["normal", "toggle"] ++
									update-command: ++
										type: string ++
										default: "" ++
										description: "Command to be executed on visibility change of ++
											cc to update the active state of the toggle button (should ++
											echo true or false)" ++
									active: ++
										type: bool ++
										default: false ++
										description: Wether the toggle button is active as default or not ++
							description: A list of actions containing a label and a command ++
					description: A button to reveal a dropdown with action-buttons ++
				buttons#<name>: ++
					type: object ++
					properties: ++
						position: ++
							type: string ++
							optional: true ++
							default: "right" ++
							description: Horizontal position of the buttons in the bar ++
							enum: ["right", "left"] ++
						actions: ++
							type: array ++
							Default values: [] ++
							Valid array values: ++
								type: object ++
								properties: ++
									label: ++
										type: string ++
										default: "label" ++
										description: Text to be displayed in button ++
									command: ++
										type: string ++
										default: "" ++
										description: "Command to be executed on click" ++
									type: ++
										type: string ++
										default: "normal" ++
										description: Type of the button ++
											Toggle buttons receive the '.active' css class and an env ++
											variable "SWAYNC_TOGGLE_STATE" is set. See example usage in the ++
											default config.json ++
										enum: ["normal", "toggle"] ++
									update-command: ++
										type: string ++
										default: "" ++
										description: "Command to be executed on visibility change of ++
											cc to update the active state of the toggle button (should ++
											echo true or false)" ++
									active: ++
										type: bool ++
										default: false ++
										description: Wether the toggle button is active as default or not ++
							description: A list of actions containing a label and a command ++
					description: A list of buttons to be displayed in the menu-button-bar ++
		*buttons-grid*++
			type: object ++
			css class: widget-buttons (access buttons with >flowbox>flowboxchild>button) ++
			properties: ++
				buttons-per-row: ++
				    type: number ++
				actions: ++
					type: array ++
					Default values: [] ++
					Valid array values: ++
						type: object ++
						properties: ++
							label: ++
								type: string ++
								default: "label" ++
								description: Text to be displayed in button ++
							command: ++
								type: string ++
								default: "" ++
								description: "Command to be executed on click" ++
							type: ++
								type: string ++
								default: "normal" ++
								description: Type of the button ++
									Toggle buttons receive the '.active' css class and an env ++
									variable "SWAYNC_TOGGLE_STATE" is set. See example usage in the ++
									default config.json ++
								enum: ["normal", "toggle"] ++
							active: ++
								type: bool ++
								default: false ++
								description: Wether the toggle button is active as default or not ++
					description: A list of actions containing a label and a command ++
			description: A grid of buttons that execute shell commands ++
		*slider*++
			type: object ++
			css class: widget-slider ++
			properties: ++
				label: ++
					type: string ++
					optional: true ++
					default: "slider" ++
					description: Text displayed in front of the slider ++
				cmd_setter: ++
					type: string ++
					optional: true ++
					default: "" ++
					description: command to set the value. Use $value to get ++
					the current value. ++
				cmd_getter: ++
					type: string ++
					optional: true ++
					default: "" ++
					description: command to get the actual value. ++
					Use $value to get the current value. ++
				min: ++
					type: integer ++
					optional: true ++
					default: 0 ++
					description: minimum value of the slider range ++
				max: ++
					type: integer ++
					optional: true ++
					default: 100 ++
					description: maximum value of the slider range ++
				min_limit: ++
					type: integer ++
					optional: true ++
					default: 0 ++
					description: limit minimum value of the slider ++
				max_limit: ++
					type: integer ++
					optional: true ++
					default: 100 ++
					description: limit maximum value of the slider ++
				value_scale: ++
					type: integer ++
					optional: true ++
					default: 0 ++
					description: scale small value, slider round digits ++
			description: general slider control ++
#START pulse-audio
		*volume*++
			type: object ++
			css class: ++
				widget-volume ++
				per-app-volume ++
			properties: ++
				label: ++
					type: string ++
					optional: true ++
					default: "Volume" ++
					description: Text displayed in front of the volume slider ++
				show-per-app: ++
					type: bool ++
					optional: true ++
					default: false ++
					description: Show per app volume control ++
				show-per-app-icon: ++
					type: bool ++
					optional: true ++
					default: false ++
					description: Show application icon in per app control ++
				show-per-app-label: ++
					type: bool ++
					optional: true ++
					default: false ++
					description: Show application name in per app control ++
				expand-per-app: ++
					type: bool ++
					optional: true ++
					default: false ++
					description: If the per app section should start expanded ++
				empty-list-label: ++
					type: string ++
					optional: true ++
					default: "No active sink input" ++
					description: Text displayed when there are not active sink inputs ++
				expand-button-label: ++
					type: string ++
					optional: true ++
					default: "⇧" ++
					description: Label displayed on button to show per app volume control ++
				collapse-button-label: ++
					type: string ++
					optional: true ++
					default: "⇩" ++
					description: Label displayed on button to hide per app volume control ++
				animation-type: ++
					type: string ++
					optional: true ++
					default: "slide_down" ++
					description: Animation type for the per app volume control ++
					enum: ["slide_down", "slide_up", "none"] ++
				animation-duration: ++
					type: integer ++
					optional: true ++
					default: 250 ++
					description: Duration of animation in milliseconds ++
			description: Slider to control pulse volume ++
#END pulse-audio
		*backlight*++
			type: object ++
			css class: widget-backlight ++
			properties: ++
				label: ++
					type: string ++
					optional: true ++
					default: "Brightness" ++
					description: Text displayed in front of the backlight slider ++
				device: ++
					type: string ++
					optional: true ++
					default: "intel_backlight" ++
					description: Device in `/sys/class/backlight` or `/sys/class/leds` ++
				subsystem: ++
					type: string ++
					optional: true ++
					default: "backlight" ++
					description: Kernel subsystem for brightness control ++
					enum: ["backlight", "leds"] ++
				min: ++
					type: integer ++
					optional: true ++
					default: 0 ++
					description: Lowest possible value for brightness ++
			description: Slider to control screen brightness ++
		*inhibitors*++
			type: object ++
			css class: widget-inhibitors ++
			properties: ++
				text: ++
					type: string ++
					optional: true ++
					default: "Inhibitors" ++
					description: The title of the widget ++
				clear-all-button: ++
					type: bool ++
					optional: true ++
					default: true ++
					description: Whether to display a "Clear All" button ++
				button-text: ++
					type: string ++
					optional: true ++
					default: "Clear All" ++
					description: "Clear All" button text ++
			description: Displayed if notifications are inhibited.

	example:
```
{
	"widget-config": {
		"title": {
			"text": "Notifications",
			"clear-all-button": true,
			"button-text": "Clear All"
		},
		"dnd": {
			"text": "Do Not Disturb"
		},
		"label": {
			"max-lines": 5,
			"text": "Label Text"
		},
		"mpris": {
			"blacklist": ["playerctld"],
			"autohide": true,
			"show-album-art": "always",
			"loop-carousel": false
		},
		"menubar": {
			"menu#power": {
				"label": "Power",
				"position": "right",
				"actions": [
					{
						"label": "Shut down",
						"command": "systemctl poweroff"
					},
					...
				]
			},
			"buttons#screenshot": {
				"position": "left",
				"actions": [
					{
						"label": "Screenshot",
						"command": "grim"
					},
					...
				]
			}
		},
		"buttons": {
			"actions": [
				{
					"label": "wifi",
					"command": "rofi-wifi-menu"
				},
				...
			]
		}
	}
}
```
#START scripting

# Scripts

*script-fail-notify* ++
	type: bool ++
	default: true ++
	description: Sends a notification if a script fails to run

*scripts* ++
	type: object ++
	script object properties: ++
		*exec*++
			type: string ++
			optional: false ++
			description: The script to run. Can also run regular shell commands.++
		*app-name*++
			type: string ++
			optional: true ++
			description: The app-name. Uses Regex.++
		*desktop-entry*++
			type: string ++
			optional: true ++
			description: The desktop-entry. Uses Regex.++
		*summary*++
			type: string ++
			optional: true ++
			description: The summary of the notification. Uses Regex.++
		*body*++
			type: string ++
			optional: true ++
			description: The body of the notification. Uses Regex.++
		*urgency*++
			type: string ++
			optional: true ++
			default: Normal ++
			values: Low, Normal, Critical ++
			description: The urgency of the notification.++
		*category*++
			type: string ++
			optional: true ++
			description: Which category the notification belongs to. Uses Regex.++
		*sound-file*++
			type: string ++
			optional: true ++
			description: Which sound file the notification requested. Uses Regex.++
		*sound-name*++
			type: string ++
			optional: true ++
			description: Which sound name the notification requested. Uses Regex.++
		*run-on*++
			type: string ++
			optional: true ++
			values: action, receive ++
			default: receive ++
			description: Whether to run this action when the notification is ++
						 received, or when an action is taken on it. ++
	description: Which scripts to check and potentially run for every ++
		notification. If the notification doesn't include one of the properties, ++
		that property will be ignored. All properties (except for exec) use regex. ++
		If all properties match the given notification, the script will be run. ++
		Only the first matching script will be run. ++
	example:
```
{
	"scripts": {
		"example-script": {
			"exec": "Your shell command or script here...",
			"app-name": "Notification app-name Regex",
			"summary": "Notification summary Regex",
			"body": "Notification body Regex",
			"urgency": "Low or Normal or Critical",
			"category": "Notification category Regex"
		}
	}
}
```

	You can also use these environment variables in your script:
```
$SWAYNC_BODY="Notification body content"
$SWAYNC_DESKTOP_ENTRY="Desktop entry"
$SWAYNC_URGENCY="Notification urgency"
$SWAYNC_TIME="Notification time"
$SWAYNC_APP_NAME="Notification app name"
$SWAYNC_CATEGORY="SwayNC notification category"
$SWAYNC_REPLACES_ID="ID of notification to replace"
$SWAYNC_ID="SwayNC notification ID"
$SWAYNC_SUMMARY="Notification summary"
$SWAYNC_HINT_[NAME]="Value of the hint [NAME]"
$SWAYNC_SOUND_NAME="The name of the requested sound"
$SWAYNC_SOUND_FILE="The file path of the requested sound"
```
#END scripting
07070100000047000081A4000000000000000000000001690B16EB00001300000000000000000000000000000000000000002A00000000SwayNotificationCenter-0.12.3/meson.buildproject('sway-notificaton-center', ['c', 'vala'],
  version: '0.12.3',
  meson_version: '>= 0.60.0',
  default_options: [ 'warning_level=2' ],
)

add_project_arguments(['-Wno-error=int-conversion'], language: 'c')
add_project_arguments(['--enable-gobject-tracing'], language: 'vala')
add_project_arguments(['--enable-checking'], language: 'vala')

cc = meson.get_compiler('c')
vala = meson.get_compiler('vala')

gnome = import('gnome')

app_resources = []

config_path = join_paths(get_option('sysconfdir'), 'xdg', 'swaync')

# Wayland protocols
protocol_dep = []
subdir('protocols')

subdir('data')
subdir('src')

datadir = get_option('datadir')
libdir = get_option('libdir')

conf_data = configuration_data()
conf_data.set('bindir', join_paths(get_option('prefix'), get_option('bindir')))

# Dbus service
configure_file(
  configuration: conf_data,
  input: 'services/dbus/org.erikreider.swaync.service.in',
  output: '@BASENAME@',
  install_dir: datadir + '/dbus-1/services'
)

# Systemd service unit
systemd = dependency('systemd', required: false)
if get_option('systemd-service')
  if systemd.found()
    systemd_service_install_dir = systemd.get_variable(pkgconfig :'systemduserunitdir')
  else
    systemd_service_install_dir = join_paths(libdir, 'systemd', 'user')
  endif

  configure_file(
    configuration: conf_data,
    input: 'services/systemd/swaync.service.in',
    output: '@BASENAME@',
    install_dir: systemd_service_install_dir
  )
endif

# Zsh completion
if get_option('zsh-completions')
  zsh_files = files(
    'completions/zsh/_swaync',
    'completions/zsh/_swaync-client',
  )
  zsh_install_dir = join_paths(datadir, 'zsh', 'site-functions')

  install_data(zsh_files, install_dir: zsh_install_dir)
endif

# Bash completion
bash_comp = dependency('bash-completion', required: false)
if get_option('bash-completions')
  bash_files = files(
    'completions/bash/swaync',
    'completions/bash/swaync-client',
  )

  if bash_comp.found()
    bash_install_dir = bash_comp.get_variable(
      pkgconfig: 'completionsdir',
      pkgconfig_define: ['datadir', datadir]
    )
  else
    bash_install_dir = join_paths(datadir, 'bash-completion', 'completions')
  endif

  install_data(bash_files, install_dir: bash_install_dir)
endif

# Fish completion
fish_comp = dependency('fish', required: false)
if get_option('fish-completions')
  fish_files = files(
    'completions/fish/swaync.fish',
    'completions/fish/swaync-client.fish',
  )

  if fish_comp.found()
    fish_install_dir = fish_comp.get_variable(
      pkgconfig: 'completionsdir',
      pkgconfig_define: ['datadir', datadir]
    )
  else
    fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d')
  endif

  install_data(fish_files, install_dir: fish_install_dir)
endif

# Man pages
if get_option('man-pages')
  scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: true)
  sed = find_program(['sed', '/usr/bin/sed'], required : true)
  if scdoc.found() and sed.found()
    scdoc_prog = find_program(scdoc.get_variable(pkgconfig :'scdoc'), native: true)

    # Remove parts of man page if necessary
    sed_command = []
    foreach option : ['pulse-audio', 'scripting']
      if get_option(option) == false
        # Removes all lines between the #START and #END lines (inclusive)
        sed_command += ['-e', '/#START @0@/,/#END @0@/d'.format(option)]
      else
        # Removes the #START and #END lines
        sed_command += ['-e', '/#START @0@/d;/#END @0@/d'.format(option)]
      endif
    endforeach

    mandir = get_option('mandir')
    man_files = [
      'swaync.1.scd',
      'swaync.5.scd',
      'swaync-client.1.scd',
    ]

    foreach filename : man_files
      topic = filename.split('.')[-3].split('/')[-1]
      section = filename.split('.')[-2]
      input = join_paths('man', filename)
      output = '@0@.@1@'.format(topic, section)
      message(mandir, section, '@0@/man@1@'.format(mandir, section))

      # Remove parts of man page if necessary
      if sed_command.length() > 0
        output_stripped = '.stripped-@0@.@1@'.format(topic, section)
        input = custom_target(
          output_stripped,
          input: input,
          output: output_stripped,
          command: [sed, sed_command],
          install: false,
          feed: true,
          capture: true,
          build_by_default: true,
          build_always_stale: true
        )
      endif

      custom_target(
        output,
        input: input,
        output: output,
        command: scdoc_prog,
        install: true,
        feed: true,
        capture: true,
        build_by_default: true,
        build_always_stale: true,
        install_dir: '@0@/man@1@'.format(mandir, section)
      )
    endforeach
  endif
endif

# Run the postinstall script when installing
meson.add_install_script('build-aux/meson/postinstall.py')
07070100000048000081A4000000000000000000000001690B16EB000002BD000000000000000000000000000000000000003000000000SwayNotificationCenter-0.12.3/meson_options.txtoption('systemd-service', type: 'boolean', value: true, description: 'Install systemd user service unit.')
option('scripting', type: 'boolean', value: true, description: 'Enable notification scripting.')
option('pulse-audio', type: 'boolean', value: true, description: 'Provide PulseAudio Widget.')
option('man-pages', type: 'boolean', value: true, description: 'Install all man pages.')
option('zsh-completions', type: 'boolean', value: true, description: 'Install zsh shell completions.')
option('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions.')
option('fish-completions', type: 'boolean', value: true, description: 'Install fish shell completions.')
07070100000049000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002800000000SwayNotificationCenter-0.12.3/protocols0707010000004A000081A4000000000000000000000001690B16EB000003E3000000000000000000000000000000000000003400000000SwayNotificationCenter-0.12.3/protocols/meson.builddep_scanner = dependency('wayland-scanner', native: true)
prog_scanner = find_program(dep_scanner.get_variable(pkgconfig: 'wayland_scanner'))

protocol_file = files(
  'xdg-activation-v1.xml',
)

protocol_sources = []
protocol_sources += custom_target(
  'xdg-activation-v1-client-protocol.h',
  command: [prog_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
  input: protocol_file,
  output: 'xdg-activation-v1-client-protocol.h',
)

output_type = 'private-code'
if dep_scanner.version().version_compare('< 1.14.91')
  output_type = 'code'
endif
protocol_sources += custom_target(
  'xdg-activation-protocol.c',
  command: [prog_scanner, output_type, '@INPUT@', '@OUTPUT@'],
  input: protocol_file,
  output: 'xdg-activation-protocol.c',
)

protocol_dep += declare_dependency(
  dependencies: [
    vala.find_library('xdg-activation-v1', dirs: meson.current_source_dir()),
    dependency('wayland-client'),
  ],
  include_directories: include_directories('.'),
  sources: protocol_sources,
)
0707010000004B000081A4000000000000000000000001690B16EB0000000F000000000000000000000000000000000000003F00000000SwayNotificationCenter-0.12.3/protocols/xdg-activation-v1.depswayland-client
0707010000004C000081A4000000000000000000000001690B16EB000007DA000000000000000000000000000000000000003F00000000SwayNotificationCenter-0.12.3/protocols/xdg-activation-v1.vapi// vim: ft=vala

namespace XDG.Activation {
    [CCode (cheader_filename = "xdg-activation-v1-client-protocol.h", cname = "struct xdg_activation_v1", cprefix = "xdg_activation_v1_")]
    public class Activation : Wl.Proxy {
        [CCode (cheader_filename = "xdg-activation-v1-client-protocol.h", cname = "xdg_activation_v1_interface")]
        public static Wl.Interface iface;
        public void set_user_data (void * user_data);
        public void * get_user_data ();
        public uint32 get_version ();

        public void destroy ();

        public Token * get_activation_token ();
        public void activate (string token, Wl.Surface surface);
    }

    [CCode (cheader_filename = "xdg-activation-v1-client-protocol.h", cname = "enum xdg_activation_token_v1_error", cprefix = "XDG_ACTIVATION_TOKEN_V1_ERROR_", has_type_id = false)]
    public enum error {
        ALREADY_USED = 0,
    }

    [CCode (cheader_filename = "xdg-activation-v1-client-protocol.h", cname = "struct xdg_activation_token_v1", cprefix = "xdg_activation_token_v1_")]
    public class Token : Wl.Proxy {
        [CCode (cheader_filename = "xdg-activation-v1-client-protocol.h", cname = "xdg_activation_token_v1_listener")]
        public static Wl.Interface iface;
        public void set_user_data (void * user_data);
        public void * get_user_data ();
        public uint32 get_version ();
        public void destroy ();

        public int add_listener (TokenListener listener, void * data);

        public void set_serial (uint32 serial, Wl.Seat seat);
        public void set_app_id (string app_id);
        public void set_surface (Wl.Surface surface);
        public void commit ();
    }

    [CCode (cname = "struct xdg_activation_token_v1_listener", has_type_id = false)]
    public struct TokenListener {
        public TokenListenerDone done;
    }

    [CCode (has_target = false, has_typedef = false)]
    public delegate void TokenListenerDone (void * data, Token activation_token, string token);
}

0707010000004D000081A4000000000000000000000001690B16EB00002338000000000000000000000000000000000000003E00000000SwayNotificationCenter-0.12.3/protocols/xdg-activation-v1.xml<?xml version="1.0" encoding="UTF-8"?>
<protocol name="xdg_activation_v1">

  <copyright>
    Copyright © 2020 Aleix Pol Gonzalez &lt;aleixpol@kde.org&gt;
    Copyright © 2020 Carlos Garnacho &lt;carlosg@gnome.org&gt;

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice (including the next
    paragraph) shall be included in all copies or substantial portions of the
    Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
  </copyright>

  <description summary="Protocol for requesting activation of surfaces">
    The way for a client to pass focus to another toplevel is as follows.

    The client that intends to activate another toplevel uses the
    xdg_activation_v1.get_activation_token request to get an activation token.
    This token is then forwarded to the client, which is supposed to activate
    one of its surfaces, through a separate band of communication.

    One established way of doing this is through the XDG_ACTIVATION_TOKEN
    environment variable of a newly launched child process. The child process
    should unset the environment variable again right after reading it out in
    order to avoid propagating it to other child processes.

    Another established way exists for Applications implementing the D-Bus
    interface org.freedesktop.Application, which should get their token under
    activation-token on their platform_data.

    In general activation tokens may be transferred across clients through
    means not described in this protocol.

    The client to be activated will then pass the token
    it received to the xdg_activation_v1.activate request. The compositor can
    then use this token to decide how to react to the activation request.

    The token the activating client gets may be ineffective either already at
    the time it receives it, for example if it was not focused, for focus
    stealing prevention. The activating client will have no way to discover
    the validity of the token, and may still forward it to the to be activated
    client.

    The created activation token may optionally get information attached to it
    that can be used by the compositor to identify the application that we
    intend to activate. This can for example be used to display a visual hint
    about what application is being started.

    Warning! The protocol described in this file is currently in the testing
    phase. Backward compatible changes may be added together with the
    corresponding interface version bump. Backward incompatible changes can
    only be done by creating a new major version of the extension.
  </description>

  <interface name="xdg_activation_v1" version="1">
    <description summary="interface for activating surfaces">
      A global interface used for informing the compositor about applications
      being activated or started, or for applications to request to be
      activated.
    </description>

    <request name="destroy" type="destructor">
      <description summary="destroy the xdg_activation object">
        Notify the compositor that the xdg_activation object will no longer be
        used.

        The child objects created via this interface are unaffected and should
        be destroyed separately.
      </description>
    </request>

    <request name="get_activation_token">
      <description summary="requests a token">
        Creates an xdg_activation_token_v1 object that will provide
        the initiating client with a unique token for this activation. This
        token should be offered to the clients to be activated.
      </description>

      <arg name="id" type="new_id" interface="xdg_activation_token_v1"/>
    </request>

    <request name="activate">
      <description summary="notify new interaction being available">
        Requests surface activation. It's up to the compositor to display
        this information as desired, for example by placing the surface above
        the rest.

        The compositor may know who requested this by checking the activation
        token and might decide not to follow through with the activation if it's
        considered unwanted.

        Compositors can ignore unknown activation tokens when an invalid
        token is passed.
      </description>
      <arg name="token" type="string" summary="the activation token of the initiating client"/>
      <arg name="surface" type="object" interface="wl_surface"
	   summary="the wl_surface to activate"/>
    </request>
  </interface>

  <interface name="xdg_activation_token_v1" version="1">
    <description summary="an exported activation handle">
      An object for setting up a token and receiving a token handle that can
      be passed as an activation token to another client.

      The object is created using the xdg_activation_v1.get_activation_token
      request. This object should then be populated with the app_id, surface
      and serial information and committed. The compositor shall then issue a
      done event with the token. In case the request's parameters are invalid,
      the compositor will provide an invalid token.
    </description>

    <enum name="error">
      <entry name="already_used" value="0"
             summary="The token has already been used previously"/>
    </enum>

    <request name="set_serial">
      <description summary="specifies the seat and serial of the activating event">
        Provides information about the seat and serial event that requested the
        token.

        The serial can come from an input or focus event. For instance, if a
        click triggers the launch of a third-party client, the launcher client
        should send a set_serial request with the serial and seat from the
        wl_pointer.button event.

        Some compositors might refuse to activate toplevels when the token
        doesn't have a valid and recent enough event serial.

        Must be sent before commit. This information is optional.
      </description>
      <arg name="serial" type="uint"
           summary="the serial of the event that triggered the activation"/>
      <arg name="seat" type="object" interface="wl_seat"
           summary="the wl_seat of the event"/>
    </request>

    <request name="set_app_id">
      <description summary="specifies the application being activated">
        The requesting client can specify an app_id to associate the token
        being created with it.

        Must be sent before commit. This information is optional.
      </description>
      <arg name="app_id" type="string"
           summary="the application id of the client being activated."/>
    </request>

    <request name="set_surface">
      <description summary="specifies the surface requesting activation">
        This request sets the surface requesting the activation. Note, this is
        different from the surface that will be activated.

        Some compositors might refuse to activate toplevels when the token
        doesn't have a requesting surface.

        Must be sent before commit. This information is optional.
      </description>
      <arg name="surface" type="object" interface="wl_surface"
	   summary="the requesting surface"/>
    </request>

    <request name="commit">
      <description summary="issues the token request">
        Requests an activation token based on the different parameters that
        have been offered through set_serial, set_surface and set_app_id.
      </description>
    </request>

    <event name="done">
      <description summary="the exported activation token">
        The 'done' event contains the unique token of this activation request
        and notifies that the provider is done.
      </description>
      <arg name="token" type="string" summary="the exported activation token"/>
    </event>

    <request name="destroy" type="destructor">
      <description summary="destroy the xdg_activation_token_v1 object">
        Notify the compositor that the xdg_activation_token_v1 object will no
        longer be used. The received token stays valid.
      </description>
    </request>
  </interface>
</protocol>
0707010000004E000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002700000000SwayNotificationCenter-0.12.3/services0707010000004F000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002C00000000SwayNotificationCenter-0.12.3/services/dbus07070100000050000081A4000000000000000000000001690B16EB00000066000000000000000000000000000000000000004D00000000SwayNotificationCenter-0.12.3/services/dbus/org.erikreider.swaync.service.in[D-BUS Service]
Name=org.freedesktop.Notifications
Exec=@bindir@/swaync
SystemdService=swaync.service
07070100000051000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002F00000000SwayNotificationCenter-0.12.3/services/systemd07070100000052000081A4000000000000000000000001690B16EB00000201000000000000000000000000000000000000004100000000SwayNotificationCenter-0.12.3/services/systemd/swaync.service.in[Unit]
Description=Swaync notification daemon
Documentation=https://github.com/ErikReider/SwayNotificationCenter
PartOf=graphical-session.target
After=graphical-session.target
# ConditionEnvironment requires systemd v247 to work correctly
ConditionEnvironment=WAYLAND_DISPLAY

[Service]
Type=dbus
BusName=org.freedesktop.Notifications
ExecStart=@bindir@/swaync
ExecReload=@bindir@/swaync-client --reload-config ; @bindir@/swaync-client --reload-css
Restart=on-failure

[Install]
WantedBy=graphical-session.target
07070100000053000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002200000000SwayNotificationCenter-0.12.3/src07070100000054000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002F00000000SwayNotificationCenter-0.12.3/src/animatedList07070100000055000081A4000000000000000000000001690B16EB000063D2000000000000000000000000000000000000004100000000SwayNotificationCenter-0.12.3/src/animatedList/animatedList.valapublic enum AnimatedListDirection {
    TOP_TO_BOTTOM, BOTTOM_TO_TOP;
}

private struct AnimationData {
    public Gtk.Adjustment vadj;
    public unowned AnimatedListItem item;
}

private struct WidgetHeights {
    int min_height;
    int nat_height;
}

private struct WidgetAlloc {
    float y;
    int height;
}

public class AnimatedList : Gtk.Widget, Gtk.Scrollable {
    public const int SCROLL_ANIMATION_DURATION = 500;

    public Gtk.Adjustment hadjustment { get; set construct; }
    public Gtk.ScrollablePolicy hscroll_policy { get; set; }
    public Gtk.Adjustment vadjustment { get; set construct; }
    public Gtk.ScrollablePolicy vscroll_policy { get; set; }

    public uint n_children { get; private set; }
    public unowned List<AnimatedListItem> children {
        get;
        private construct set;
    }
    public unowned List<unowned AnimatedListItem> visible_children {
        get;
        private construct set;
    }

    /**
     * Indicates if new / removed children should display an expand and shrink
     * animation.
     */
    public bool transition_children { get; construct set; }
    /** Whether or not the list should display its items in a stack or not */
    public bool use_card_animation { get; construct set; }
    /** The direction that the items should flow in */
    public AnimatedListDirection direction { get; construct set; }
    /** Scroll to the latest item added to the list */
    public bool scroll_to_append { get; construct set; }
    /** The default item reveal animation type */
    public AnimatedListItem.RevealAnimationType animation_reveal_type {
        get;
        construct set;
    }
    /** The default item animation type */
    public AnimatedListItem.ChildAnimationType animation_child_type {
        get;
        construct set;
    }

    // Scroll bottom animation
    Adw.CallbackAnimationTarget scroll_btm_target;
    Adw.TimedAnimation scroll_btm_anim;
    AnimationData ?scroll_btm_anim_data = null;

    // Scroll top animation
    Adw.CallbackAnimationTarget scroll_top_target;
    Adw.TimedAnimation scroll_top_anim;
    AnimationData ?scroll_top_anim_data = null;

    // Adding an item to the top compensation
    Adw.CallbackAnimationTarget scroll_comp_target;
    Adw.TimedAnimation scroll_comp_anim;
    AnimationData ?scroll_comp_anim_data = null;

    // When true, the size_allocate method will scroll to the top/bottom
    private bool set_initial_scroll_value = false;
    private float fade_distance = 0.0f;
    private unowned Gtk.Settings settings = Gtk.Settings.get_default ();

    construct {
        hadjustment = null;

        children = new List<AnimatedListItem> ();
        visible_children = new List<unowned AnimatedListItem> ();

        notify["vadjustment"].connect (() => {
            if (vadjustment != null) {
                vadjustment.value_changed.connect (() => {
                    queue_allocate ();
                });
            }
        });

        map.connect (() => {
            // Ensures that the initial scroll position gets set after
            // GTK recalculates the layout
            Idle.add_once (() => {
                set_initial_scroll_value = true;
                queue_allocate ();
            });
        });

        set_overflow (Gtk.Overflow.HIDDEN);

        scroll_btm_target = new Adw.CallbackAnimationTarget (scroll_bottom_value_cb);
        scroll_btm_anim = new Adw.TimedAnimation (
            this, 0.0, 1.0, SCROLL_ANIMATION_DURATION, scroll_btm_target);
        scroll_btm_anim.set_easing (Adw.Easing.EASE_OUT_QUINT);

        scroll_top_target = new Adw.CallbackAnimationTarget (scroll_top_value_cb);
        scroll_top_anim = new Adw.TimedAnimation (
            this, 0.0, 1.0, SCROLL_ANIMATION_DURATION, scroll_top_target);
        scroll_top_anim.set_easing (Adw.Easing.EASE_OUT_QUINT);

        scroll_comp_target = new Adw.CallbackAnimationTarget (scroll_comp_value_cb);
        scroll_comp_anim = new Adw.TimedAnimation (
            this, 0.0, 1.0, SCROLL_ANIMATION_DURATION, scroll_comp_target);
        scroll_comp_anim.set_easing (Adw.Easing.EASE_OUT_QUINT);
    }

    public AnimatedList () {
        Object (
            css_name : "animatedlist",
            accessible_role : Gtk.AccessibleRole.LIST,
            transition_children : true,
            use_card_animation: true,
            direction: AnimatedListDirection.TOP_TO_BOTTOM,
            scroll_to_append: false
        );
    }

    public override void dispose () {
        foreach (AnimatedListItem child in children) {
            transition_children = false;
            remove.begin (child, false);
        }

        base.dispose ();
    }

    public bool get_border (out Gtk.Border border) {
        border = Gtk.Border ();
        return false;
    }

    protected override Gtk.SizeRequestMode get_request_mode () {
        foreach (unowned AnimatedListItem item in children) {
            if (item.get_request_mode () != Gtk.SizeRequestMode.CONSTANT_SIZE) {
                return Gtk.SizeRequestMode.HEIGHT_FOR_WIDTH;
            }
        }
        return Gtk.SizeRequestMode.CONSTANT_SIZE;
    }

    protected override void compute_expand_internal (out bool hexpand_p,
                                                     out bool vexpand_p) {
        hexpand_p = false;
        vexpand_p = false;

        foreach (unowned AnimatedListItem item in children) {
            hexpand_p |= item.compute_expand (Gtk.Orientation.HORIZONTAL);
            vexpand_p |= item.compute_expand (Gtk.Orientation.VERTICAL);
        }
    }

    private float get_fade_distance (int height) {
        switch (direction) {
            case AnimatedListDirection.TOP_TO_BOTTOM:
                return height - fade_distance;
            case AnimatedListDirection.BOTTOM_TO_TOP:
                return fade_distance;
        }
        return 0;
    }

    private void compute_height (int width,
                                 int height,
                                 out int total_height,
                                 out WidgetAlloc[] child_heights) {
        total_height = 0;
        child_heights = new WidgetAlloc[n_children];

        fade_distance = 0;

        int num_vexpand_children = 0;
        WidgetHeights measured_height = WidgetHeights ();
        WidgetHeights[] heights = new WidgetHeights[n_children];
        int total_min = 0;
        int total_nat = 0;

        int i = 0;
        foreach (AnimatedListItem child in children) {
            if (!child.should_layout ()) {
                continue;
            }

            // Get the largest minimum height and use it for the fade distance
            int nat_width;
            // First, get the minimum width of our widget
            child.measure (Gtk.Orientation.HORIZONTAL, -1,
                           null, out nat_width, null, null);
            // Now use the natural width to retrieve the minimum and
            // natural height to display.
            int min_height, nat_height;
            child.measure (Gtk.Orientation.VERTICAL, nat_width,
                           out min_height, out nat_height, null, null);
            fade_distance = float.max (
                fade_distance,
                int.min (min_height, nat_height)
            );

            int min, nat;
            child.measure (Gtk.Orientation.VERTICAL, width,
                           out min, out nat, null, null);
            heights[i] = WidgetHeights () {
                min_height = min,
                nat_height = nat,
            };
            total_min += min;
            total_nat += nat;

            if (child.compute_expand (Gtk.Orientation.VERTICAL)) {
                num_vexpand_children++;
            }

            i++;
        }

        bool allocate_nat = false;
        int extra_height = 0;
        if (height >= measured_height.nat_height) {
            allocate_nat = true;
            extra_height = height - measured_height.nat_height;
        } else {
            warn_if_reached ();
        }

        int y = 0;
        i = 0;
        foreach (AnimatedListItem child in children) {
            WidgetHeights computed_height = heights[i];
            WidgetAlloc child_allocation = WidgetAlloc () {
                y = 0,
                height = computed_height.min_height,
            };
            if (allocate_nat) {
                child_allocation.height = computed_height.nat_height;
            }

            if (child.compute_expand (Gtk.Orientation.VERTICAL)) {
                child_allocation.height += extra_height / num_vexpand_children;
            }

            child_allocation.y = y;
            child_heights[i] = child_allocation;

            total_height += child_allocation.height;
            y += child_allocation.height;
            i++;
        }

        const float LIMIT = 0.2f;
        if (fade_distance == 0) {
            fade_distance = height * LIMIT;
        } else {
            // Make sure that the fade distance isn't larger than the height
            fade_distance = float.min (fade_distance, height * LIMIT);
        }

        // The padding to add to the bottom/top of the list to compensate
        // for the fade, but only when the list is large enough to allow
        // for scrolling.
        if (should_card_animate () && vadjustment != null && total_height > height) {
            total_height += (int) fade_distance;
        }
    }

    protected override void size_allocate (int width,
                                           int height,
                                           int baseline) {
        // Recalculate which children are visible, so clear the old list
        while (!visible_children.is_empty ()) {
            visible_children.delete_link (visible_children.nth (0));
        }
        warn_if_fail (visible_children.is_empty ());

        // Save the already computed widget heights. We need the total height
        // for calculating the reversed list animation, so two loops through the
        // widgets is necessary...
        int total_height;
        WidgetAlloc[] heights;
        compute_height (width, height, out total_height, out heights);

        bool is_reversed = direction == AnimatedListDirection.BOTTOM_TO_TOP;
        bool has_scroll = total_height > height;

        float scroll_y = 0.0f;
        if (vadjustment != null) {
            scroll_y = (float) vadjustment.value;
            // Set the initial scroll value to the top or bottom
            // if the user hasn't scrolled yet.
            if (has_scroll && set_initial_scroll_value) {
                if (get_mapped ()) {
                    set_initial_scroll_value = false;
                }
                switch (direction) {
                    case AnimatedListDirection.TOP_TO_BOTTOM:
                        scroll_y = 0;
                        break;
                    case AnimatedListDirection.BOTTOM_TO_TOP:
                        scroll_y = total_height - height;
                        break;
                }
            }
        }
        total_height = int.max (height, total_height);

        // Allocate the size and position of each item
        uint index = 0;
        foreach (AnimatedListItem child in children) {
            if (!child.should_layout ()) {
                index++;
                continue;
            }

            WidgetAlloc child_allocation = heights[index];
            int child_height = child_allocation.height;

            float scale = 1.0f;
            float x = 0;
            float y = child_allocation.y - scroll_y;
            if (is_reversed) {
                y = total_height - child_height - child_allocation.y - scroll_y;
            }
            float opacity = 1.0f;

            bool skip_child = true;
            if (y < height && child_height + y > 0) {
                skip_child = false;

                // Deck of cards effect
                if (should_card_animate () && has_scroll) {
                    float item_center = y + child_height * 0.5f;
                    // Compensate for very tall items being faded out before seeing the top
                    if (child_height > height) {
                        item_center = y + (is_reversed ? child_height : 0);
                    }
                    // The cut off where to start fade away
                    float local_fade_distance = get_fade_distance (height);
                    if ((!is_reversed && item_center > local_fade_distance)
                        || (is_reversed && item_center < local_fade_distance)) {
                        // The distance from the fade edge
                        float dist = Math.fabsf (item_center - local_fade_distance);

                        // Hide when half way across the "circle".
                        // A little trigonometry never killed anybody :-)
                        float radius = fade_distance;
                        if (dist < radius) {
                            float angle = Math.atanf (dist / radius);
                            // The Y value within the circle (dot product)
                            float new_y = Math.sinf (angle) * radius;
                            // The ratio between the untransformed height and the dot product height
                            scale = 1.0f - (new_y / height * 2);
                            // Center the item in the X axis
                            x = (width - (width * scale)) * 0.5f;
                            // Calculate the new distance from the start of the fade
                            // NOTE: Reverse the direction of the animation
                            // depending on the list direction.
                            y += (float) (dist * Math.sin (angle) * (is_reversed ? 1 : -1));
                            opacity = 1.0f - (dist / radius * 2);

                            skip_child |= opacity <= 0.1;
                        } else {
                            skip_child = true;
                        }
                    }
                }
            }

            // Only display visible items
            if (!skip_child
                && y < height && child_height + y > 0) {
                // Prepend the child so that the child can be rendered first
                // (to maintain a reversed z-index)
                visible_children.prepend (child);
            }

            Gsk.Transform transform = new Gsk.Transform ()
                 .translate (Graphene.Point ().init (x, y))
                 .scale (scale, scale);
            child.allocate (width, child_height, baseline, transform);
            child.set_opacity (opacity);

            index++;
        }

        if (vadjustment != null) {
            vadjustment.configure (scroll_y,
                                   0, total_height,
                                   height * 0.1,
                                   height * 0.9,
                                   height);
        }
    }

    protected override void measure (Gtk.Orientation orientation,
                                     int for_size,
                                     out int minimum,
                                     out int natural,
                                     out int minimum_baseline,
                                     out int natural_baseline) {
        minimum = 0;
        natural = 0;
        minimum_baseline = -1;
        natural_baseline = -1;

        int min = 0, nat = 0;
        int largest_min = 0, largest_nat = 0;
        foreach (AnimatedListItem child in children) {
            if (!child.should_layout ()) {
                continue;
            }

            int child_min, child_nat;
            child.measure (orientation, for_size,
                           out child_min, out child_nat, null, null);
            min += child_min;
            nat += child_nat;
            largest_min = int.max (largest_min, child_min);
            largest_nat = int.max (largest_min, child_nat);
        }

        switch (orientation) {
            case Gtk.Orientation.HORIZONTAL:
                minimum = largest_min;
                natural = largest_nat;
                break;
            case Gtk.Orientation.VERTICAL:
                minimum = min;
                natural = nat;
                break;
        }
    }

    protected override void snapshot (Gtk.Snapshot snapshot) {
        // Only render the visible items, backwards to retain a valid z-index
        foreach (unowned AnimatedListItem child in visible_children) {
            if (!child.should_layout ()) {
                continue;
            }
            snapshot_child (child, snapshot);
        }
    }

    private Gtk.Adjustment clone_adjustment (Gtk.Adjustment original) {
        return new Gtk.Adjustment (
            original.get_value (),
            original.get_lower (),
            original.get_upper (),
            original.get_step_increment (),
            original.get_page_increment (),
            original.get_page_size ()
        );
    }

    private void scroll_bottom_value_cb (double value) {
        vadjustment.set_value (
            scroll_btm_anim_data.vadj.upper - scroll_btm_anim_data.vadj.page_size
            + scroll_btm_anim_data.item.get_height ());
    }

    private void play_scroll_bottom_anim (AnimatedListItem item) {
        scroll_btm_anim_data = AnimationData () {
            item = item,
            vadj = clone_adjustment (vadjustment),
        };
        scroll_top_anim.duration = item.animation_duration;
        scroll_top_anim.easing = item.animation_easing;
        scroll_btm_anim.value_from = 0.0;
        scroll_btm_anim.value_to = 1.0;
        scroll_btm_anim.play ();
    }

    private void scroll_top_value_cb (double value) {
        vadjustment.set_value (
            Adw.lerp (scroll_top_anim_data.vadj.value, 0, value));
    }

    private void play_scroll_top_anim (AnimatedListItem item) {
        scroll_top_anim_data = AnimationData () {
            item = item,
            vadj = clone_adjustment (vadjustment),
        };
        scroll_top_anim.duration = item.animation_duration;
        scroll_top_anim.easing = item.animation_easing;
        scroll_top_anim.value_from = 0.0;
        scroll_top_anim.value_to = 1.0;
        scroll_top_anim.play ();
    }

    private void scroll_comp_value_cb (double value) {
        vadjustment.set_value (
            scroll_comp_anim_data.vadj.value
            + scroll_comp_anim_data.item.get_height ());
    }

    private void play_scroll_comp_anim (AnimatedListItem item) {
        scroll_comp_anim_data = AnimationData () {
            item = item,
            vadj = clone_adjustment (vadjustment),
        };
        scroll_comp_anim.duration = item.animation_duration;
        scroll_comp_anim.easing = Adw.Easing.LINEAR;
        scroll_comp_anim.value_from = 0.0;
        scroll_comp_anim.value_to = 1.0;
        scroll_comp_anim.play ();
    }

    private AnimatedListItem get_list_item (Gtk.Widget widget) {
        AnimatedListItem item;
        if (widget is AnimatedListItem) {
            item = widget as AnimatedListItem;
        } else {
            item = new AnimatedListItem ();
            item.child = widget;

            // Set the defaults
            item.animation_reveal_type = animation_reveal_type;
            item.animation_child_type = animation_child_type;

            widget.unparent ();
            widget.set_parent (item);
        }
        return item;
    }

    /**
     * Inserts a widget last into the list depending on direction:
     * TOP_TO_BOTTOM: Bottom
     * BOTTOM_TO_TOP: Top
     */
    public async AnimatedListItem ?prepend (Gtk.Widget widget) {
        if (widget == null || widget.parent != null) {
            warn_if_reached ();
            return null;
        }

        AnimatedListItem item = get_list_item (widget);
        item.unparent ();
        item.insert_before (this, null); // append

        children.append (item);
        n_children++;

        // Fixes the lack of auto-scrolling when scrolled at the bottom
        // and a new item gets added
        if (direction == AnimatedListDirection.TOP_TO_BOTTOM
            && vadjustment.value == vadjustment.upper - vadjustment.page_size) {
            play_scroll_bottom_anim (item);
        } else if (direction == AnimatedListDirection.BOTTOM_TO_TOP) {
            // Compensate for the scrolling when adding an item to the top of the list
            play_scroll_comp_anim (item);
        }

        yield item.added (transition_children);

        return item;
    }

    /**
     * Inserts a widget first into the list depending on direction:
     * TOP_TO_BOTTOM: Top
     * BOTTOM_TO_TOP: Bottom
     */
    public async AnimatedListItem ?append (Gtk.Widget widget) {
        if (widget == null || widget.parent != null) {
            warn_if_reached ();
            return null;
        }

        AnimatedListItem item = get_list_item (widget);
        item.unparent ();
        item.insert_after (this, null); // prepend

        children.prepend (item);
        n_children++;

        // Fixes the lack of auto-scrolling when scrolled at the bottom
        // and a new item gets added
        if (direction == AnimatedListDirection.BOTTOM_TO_TOP
            && vadjustment.value == vadjustment.upper - vadjustment.page_size) {
            play_scroll_bottom_anim (item);
        } else if (!scroll_to_append && direction == AnimatedListDirection.TOP_TO_BOTTOM) {
            // Compensate for the scrolling when adding an item to the top of the list
            play_scroll_comp_anim (item);
        } else if (scroll_to_append) {
            // Scrolls to the item if enabled
            switch (direction) {
                case AnimatedListDirection.TOP_TO_BOTTOM :
                    play_scroll_top_anim (item);
                    break;
                case AnimatedListDirection.BOTTOM_TO_TOP :
                    play_scroll_bottom_anim (item);
                    break;
            }
        }

        yield item.added (transition_children);

        return item;
    }

    private AnimatedListItem ?try_get_ancestor (Gtk.Widget widget) {
        AnimatedListItem item;
        if (widget is AnimatedListItem) {
            item = widget as AnimatedListItem;
        } else if (widget.parent is AnimatedListItem) {
            item = widget.parent as AnimatedListItem;
        } else {
            unowned Gtk.Widget ?ancestor
                = widget.get_ancestor (typeof (AnimatedListItem));
            if (!(ancestor is AnimatedListItem)) {
                warning ("Widget %p of type  \"%s\" is not an ancestor of %s!",
                         widget, widget.get_type ().name (),
                         typeof (AnimatedListItem).name ());
                return null;
            }
            item = ancestor as AnimatedListItem;
        }

        if (item.parent != this) {
            warn_if_reached ();
            return null;
        }
        return item;
    }

    public async bool remove (Gtk.Widget widget, bool transition) {
        if (widget == null) {
            warn_if_reached ();
            return false;
        }

        AnimatedListItem ?item = try_get_ancestor (widget);
        if (item == null) {
            return false;
        }

        // Will unparent itself when done animating
        bool result = yield item.removed (transition_children && transition);

        item.unparent ();
        children.remove (item);
        n_children--;
        queue_resize ();
        return result;
    }

    public bool move_to_beginning (Gtk.Widget widget, bool scroll_to) {
        if (widget == null) {
            warn_if_reached ();
            return false;
        }

        AnimatedListItem ?item = try_get_ancestor (widget);
        if (item == null) {
            warn_if_reached ();
            return false;
        }

        // move to the beginning of the list
        item.insert_after (this, null);
        children.remove (item);
        children.prepend (item);

        queue_resize ();

        if (scroll_to) {
            scroll_to_top ();
        }

        return true;
    }

    private uint scroll_to_source_id = 0;
    public void scroll_to_top () {
        if (scroll_to_source_id > 0) {
            Source.remove (scroll_to_source_id);
        }
        scroll_to_source_id = Idle.add_once (() => {
            scroll_to_source_id = 0;

            unowned AnimatedListItem ?item = get_first_item ();
            return_if_fail (item != null);
            switch (direction) {
                case AnimatedListDirection.TOP_TO_BOTTOM :
                    play_scroll_top_anim (item);
                    break;
                case AnimatedListDirection.BOTTOM_TO_TOP :
                    play_scroll_bottom_anim (item);
                    break;
            }
        });
    }

    public bool is_empty () {
        return children.is_empty ();
    }

    public unowned AnimatedListItem ?get_first_item () {
        if (children.is_empty ()) {
            return null;
        }
        return children.first ().data;
    }

    public unowned AnimatedListItem ?get_last_item () {
        if (children.is_empty ()) {
            return null;
        }
        return children.last ().data;
    }

    private bool should_card_animate () {
        bool value = this.use_card_animation;
        if (settings != null) {
            value &= settings.gtk_enable_animations;
        }
        return value;
    }
}
07070100000056000081A4000000000000000000000001690B16EB0000215D000000000000000000000000000000000000004500000000SwayNotificationCenter-0.12.3/src/animatedList/animatedListItem.valapublic class AnimatedListItem : Gtk.Widget {
    public const int DEFAULT_ANIMATION_DURATION = 350;

    public enum RevealAnimationType {
        NONE, SLIDE, SLIDE_WITH
    }

    public enum ChildAnimationType {
        NONE, SLIDE_FROM_LEFT, SLIDE_FROM_RIGHT
    }

    protected unowned Gtk.Widget _child = null;
    public unowned Gtk.Widget child {
        get {
            return _child;
        }
        set {
            _child = value;
            if (_child != null) {
                _child.unparent ();
                _child.set_parent (this);
            }
        }
    }
    public int animation_duration { get; construct set; }
    public Adw.Easing animation_easing { get; construct set; }
    public RevealAnimationType animation_reveal_type { get; construct set; }
    public ChildAnimationType animation_child_type { get; construct set; }
    public bool animation_child_fade { get; construct set; }
    public bool destroying { get; private set; default = false; }

    private Adw.TimedAnimation animation;
    private double animation_value = 1.0;
    private ulong animation_done_cb_id = 0;
    private unowned SourceFunc ?removed_cb = null;
    private unowned SourceFunc ?added_cb = null;

    public AnimatedListItem () {
        Object (
            css_name : "animatedlistitem",
            accessible_role : Gtk.AccessibleRole.LIST_ITEM,
            overflow: Gtk.Overflow.HIDDEN,
            animation_duration: DEFAULT_ANIMATION_DURATION,
            animation_easing: Adw.Easing.EASE_OUT_QUINT,
            animation_reveal_type: RevealAnimationType.SLIDE,
            animation_child_type: ChildAnimationType.SLIDE_FROM_RIGHT,
            animation_child_fade: true
        );

        Adw.CallbackAnimationTarget target = new Adw.CallbackAnimationTarget (set_animation_value);
        animation = new Adw.TimedAnimation (this, 0.0, 1.0, animation_duration, target);
        bind_property ("animation-easing",
                       animation, "easing",
                       BindingFlags.SYNC_CREATE, null, null);
    }

    public override void dispose () {
        if (child != null) {
            child.unparent ();
            child = null;
        }

        base.dispose ();
    }

    public override Gtk.SizeRequestMode get_request_mode () {
        return Gtk.SizeRequestMode.HEIGHT_FOR_WIDTH;
    }

    public override void size_allocate (int width,
                                        int height,
                                        int baseline) {
        if (child == null || !child.should_layout ()) {
            return;
        }

        if (animation_value >= 1) {
            child.allocate (width, height, baseline, null);
        } else if (animation_value < 0) {
            return;
        }

        int child_width = width;
        int child_height = height;
        if (animation_value < 1.0) {
            int min, nat;
            child.measure (Gtk.Orientation.VERTICAL, width,
                           out min, out nat, null, null);
            if (Math.ceil (nat * animation_value) == height) {
                child_height = nat;
            } else if (Math.ceil (min * animation_value) == height) {
                child_height = min;
            } else {
                double d = Math.floor (height / animation_value);
                child_height = int.min ((int) d, int.MAX);
            }
        }

        Gsk.Transform transform = new Gsk.Transform ();
        switch (animation_reveal_type) {
            case RevealAnimationType.SLIDE_WITH:
                transform = transform.translate_3d (
                    Graphene.Point3D ().init (0, height - child_height, 0)
                );
                break;
            case RevealAnimationType.SLIDE:
            case RevealAnimationType.NONE:
                break;
        }
        switch (animation_child_type) {
            case ChildAnimationType.SLIDE_FROM_RIGHT:
                transform = transform.translate_3d (
                    Graphene.Point3D ()
                     .init (child_width * (float) (1 - animation_value), 0, 0)
                );
                break;
            case ChildAnimationType.SLIDE_FROM_LEFT:
                transform = transform.translate_3d (
                    Graphene.Point3D ()
                     .init (-child_width * (float) (1 - animation_value), 0, 0)
                );
                break;
            case ChildAnimationType.NONE:
                break;
        }

        child.allocate (child_width, child_height, -1, transform);
    }

    public override void measure (Gtk.Orientation orientation,
                                  int for_size,
                                  out int minimum,
                                  out int natural,
                                  out int minimum_baseline,
                                  out int natural_baseline) {
        minimum = 0;
        natural = 0;
        minimum_baseline = -1;
        natural_baseline = -1;

        if (child == null || !child.should_layout ()) {
            return;
        }

        child.measure (orientation, for_size,
                       out minimum, out natural, null, null);

        switch (orientation) {
            case Gtk.Orientation.HORIZONTAL:
                break;
            case Gtk.Orientation.VERTICAL:;
                minimum = (int) Math.ceil (minimum * animation_value);
                natural = (int) Math.ceil (natural * animation_value);
                break;
        }
    }

    public override void snapshot (Gtk.Snapshot snapshot) {
        if (!child.should_layout ()) {
            return;
        }

        if (animation_child_fade) {
            snapshot.push_opacity (animation_value);
        }

        snapshot_child (child, snapshot);

        if (animation_child_fade) {
            snapshot.pop ();
        }
    }

    private void set_animation_value (double value) {
        animation_value = value;
        queue_resize ();
    }

    private inline void remove_animation_done_cb () {
        if (animation_done_cb_id != 0) {
            animation.disconnect (animation_done_cb_id);
            animation_done_cb_id = 0;
        }
    }

    delegate void animation_done (Adw.Animation animation);

    private void set_animation_done_cb (animation_done handler) {
        remove_animation_done_cb ();
        animation_done_cb_id = animation.done.connect (handler);
    }

    private void added_finished_cb () {
        if (added_cb != null) {
            added_cb ();
            added_cb = null;
        }
    }

    public async void added (bool transition) {
        if (added_cb != null) {
            // Already running animation
            return;
        }

        remove_animation_done_cb ();

        if (get_mapped () && transition) {
            set_animation_done_cb (added_finished_cb);
            added_cb = added.callback;
            animation.value_from
                = animation.state == Adw.AnimationState.PLAYING
                    ? animation_value : 0.0;
            animation.value_from = animation.value;
            animation.value_to = 1.0;
            animation.play ();
            yield;
        } else {
            set_animation_value (1.0);
            added_finished_cb ();
        }
    }

    private void removed_finised_cb () {
        if (removed_cb != null) {
            removed_cb ();
            removed_cb = null;
        }
    }

    public async bool removed (bool transition) {
        if (removed_cb != null) {
            // Already running animation
            return false;
        }

        remove_animation_done_cb ();
        // Call the `added` callback if it's still stuck in the yield waiting state
        added_finished_cb ();

        set_can_focus (false);
        set_can_target (false);

        if (get_mapped () && transition) {
            set_animation_done_cb (removed_finised_cb);
            removed_cb = removed.callback;
            animation.value_from
                = animation.state == Adw.AnimationState.PLAYING
                    ? animation_value : 1.0;
            animation.value_to = 0.0;
            destroying = true;
            animation.play ();
            yield;
        } else {
            set_animation_value (0.0);
            removed_finised_cb ();
        }

        if (child != null) {
            child.unparent ();
            child = null;
        }
        // Fixes the animation keeping a reference of the widget
        animation = null;

        return true;
    }
}
07070100000057000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002E00000000SwayNotificationCenter-0.12.3/src/blankWindow07070100000058000081A4000000000000000000000001690B16EB00001048000000000000000000000000000000000000003F00000000SwayNotificationCenter-0.12.3/src/blankWindow/blankWindow.valanamespace SwayNotificationCenter {
    public class BlankWindow : Gtk.ApplicationWindow {
        public unowned Gdk.Monitor monitor { get; private set; }

        private Gtk.GestureClick blank_window_gesture;
        private bool blank_window_down = false;
        private bool blank_window_in = false;

        public BlankWindow (Gdk.Monitor monitor) {
            Object (css_name: "blankwindow");
            this.monitor = monitor;

            blank_window_gesture = new Gtk.GestureClick ();
            ((Gtk.Widget) this).add_controller (blank_window_gesture);
            blank_window_gesture.touch_only = false;
            blank_window_gesture.exclusive = true;
            blank_window_gesture.button = Gdk.BUTTON_PRIMARY;
            blank_window_gesture.propagation_phase = Gtk.PropagationPhase.BUBBLE;
            blank_window_gesture.pressed.connect ((n_press, x, y) => {
                blank_window_in = true;
                blank_window_down = true;
            });
            blank_window_gesture.released.connect ((n_press, x, y) => {
                // Emit released
                if (!blank_window_down) {
                    return;
                }
                blank_window_down = false;
                if (blank_window_in) {
                    try {
                        swaync_daemon.set_visibility (false);
                    } catch (Error e) {
                        stderr.printf ("ControlCenter BlankWindow Click Error: %s\n",
                                       e.message);
                    }
                }

                if (blank_window_gesture.get_current_sequence () == null) {
                    blank_window_in = false;
                }
            });
            blank_window_gesture.update.connect ((gesture, sequence) => {
                Gtk.GestureSingle gesture_single = (Gtk.GestureSingle) gesture;
                if (sequence != gesture_single.get_current_sequence ()) {
                    return;
                }
                // Calculate if the clicked coords intersect other monitors
                double x, y;
                gesture.get_point (sequence, out x, out y);
                Graphene.Point click_point = Graphene.Point ()
                     .init ((float) x, (float) y);
                Graphene.Rect ?bounds = null;
                this.compute_bounds (this, out bounds);
                if (bounds != null && bounds.contains_point (click_point)) {
                    blank_window_in = false;
                }
            });
            blank_window_gesture.cancel.connect (() => {
                blank_window_down = false;
            });

            if (!GtkLayerShell.is_supported ()) {
                stderr.printf ("GTKLAYERSHELL IS NOT SUPPORTED!\n");
                stderr.printf ("Swaync only works on Wayland!\n");
                stderr.printf ("If running waylans session, try running:\n");
                stderr.printf ("\tGDK_BACKEND=wayland swaync\n");
                Process.exit (1);
            }
            GtkLayerShell.init_for_window (this);
            GtkLayerShell.set_namespace (this, "swaync-control-center");
            GtkLayerShell.set_monitor (this, monitor);

            GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.TOP, true);
            GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.BOTTOM, true);
            GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.LEFT, true);
            GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.RIGHT, true);

            GtkLayerShell.set_exclusive_zone (this, -1);

            GtkLayerShell.set_layer (
                this, ConfigModel.instance.control_center_layer.to_layer ());

            add_css_class ("blank-window");
        }

        protected override void snapshot (Gtk.Snapshot snapshot) {
            // HACK: Fixes fully transparent windows not being mapped
            Gdk.RGBA color = Gdk.RGBA () {
                red = 0,
                green = 0,
                blue = 0,
                alpha = 0,
            };
            snapshot.append_color (color, Graphene.Rect.zero ());
            base.snapshot (snapshot);
        }
    }
}
07070100000059000081A4000000000000000000000001690B16EB00003942000000000000000000000000000000000000002E00000000SwayNotificationCenter-0.12.3/src/client.valapublic struct SwayncDaemonData {
    public bool dnd;
    public bool cc_open;
    public uint count;
    public bool inhibited;
}

[DBus (name = "org.erikreider.swaync.cc")]
interface CcDaemon : Object {
    public abstract bool reload_css () throws Error;

    public abstract void reload_config () throws Error;

    public abstract void hide_latest_notifications (bool close) throws DBusError, IOError;

    public abstract void hide_all_notifications () throws DBusError, IOError;

    public abstract void close_all_notifications () throws DBusError, IOError;

    public abstract uint notification_count () throws DBusError, IOError;

    public abstract bool get_dnd () throws DBusError, IOError;

    public abstract void set_dnd (bool state) throws DBusError, IOError;

    public abstract bool get_visibility () throws DBusError, IOError;

    public abstract void toggle_visibility () throws DBusError, IOError;

    public abstract bool toggle_dnd () throws DBusError, IOError;

    public abstract void set_visibility (bool value) throws DBusError, IOError;

    public abstract void latest_invoke_action (uint32 action_index) throws DBusError, IOError;

    public abstract bool set_cc_monitor (string monitor) throws DBusError, IOError;
    public abstract bool set_noti_window_monitor (string monitor) throws DBusError, IOError;

    [DBus (name = "GetSubscribeData")]
    public abstract SwayncDaemonData get_subscribe_data () throws Error;

    public signal void subscribe_v2 (uint count, bool dnd, bool cc_open, bool inhibited);

    public abstract bool add_inhibitor (string application_id) throws DBusError, IOError;
    public abstract bool remove_inhibitor (string application_id) throws DBusError, IOError;
    public abstract uint number_of_inhibitors () throws DBusError, IOError;
    public abstract bool is_inhibited () throws DBusError, IOError;
    public abstract bool clear_inhibitors () throws DBusError, IOError;
}

private CcDaemon cc_daemon = null;

private void print_help (string[] args) {
    print ("Usage:\n");
    print ("  %s <OPTION>\n".printf (args[0]));
    print ("Help:\n");
    print ("  -h, \t --help \t\t\t Show help options\n");
    print ("  -v, \t --version \t\t\t Prints version\n");
    print ("Options:\n");
    print ("  -R, \t --reload-config \t\t Reload the config file\n");
    print ("  -rs, \t --reload-css \t\t\t Reload the css file. Location change requires restart\n");
    print ("  -t, \t --toggle-panel \t\t Toggle the notification panel\n");
    print ("  -op, \t --open-panel \t\t\t Opens the notification panel\n");
    print ("  -cp, \t --close-panel \t\t\t Closes the notification panel\n");
    print ("  -d, \t --toggle-dnd \t\t\t Toggle and print the current dnd state\n");
    print ("  -D, \t --get-dnd \t\t\t Print the current dnd state\n");
    print ("  -dn, \t --dnd-on \t\t\t Turn dnd on and print the new dnd state\n");
    print ("  -df, \t --dnd-off \t\t\t Turn dnd off and print the new dnd state\n");
    print ("  -I, \t --get-inhibited \t\t Print if currently inhibited or not\n");
    print ("  -In, \t --get-num-inhibitors \t\t Print number of inhibitors\n");
    print ("  -Ia, \t --inhibitor-add [APP_ID] \t Add an inhibitor\n");
    print ("  -Ir, \t --inhibitor-remove [APP_ID] \t Remove an inhibitor\n");
    print ("  -Ic, \t --inhibitors-clear \t\t Clears all inhibitors\n");
    print ("  -c, \t --count \t\t\t Print the current notification count\n");
    print (
        "      \t --hide-latest \t\t\t Hides latest notification. Still shown in Control Center\n");
    print ("      \t --hide-all \t\t\t Hides all notifications. Still shown in Control Center\n");
    print ("      \t --close-latest \t\t Closes latest notification\n");
    print ("  -C, \t --close-all \t\t\t Closes all notifications\n");
    print ("  -a, \t --action [ACTION_INDEX]\t " +
           "Invokes the action [ACTION_INDEX] of the latest notification\n");
    print ("  -sw, \t --skip-wait \t\t\t Doesn't wait when swaync hasn't been started\n");
    print ("  -s, \t --subscribe \t\t\t Subscribe to notification add and close events\n");
    print ("  -swb,  --subscribe-waybar \t\t Subscribe to notification add and close events "
           + "with waybar support. Read README for example\n");
    print ("      \t --change-cc-monitor \t\t Changes the preferred control center monitor"
           + " (resets on config reload)\n");
    print ("      \t --change-noti-monitor \t\t Changes the preferred notification monitor"
           + " (resets on config reload)\n");
}

private void on_subscribe (uint count, bool dnd, bool cc_open, bool inhibited) {
    stdout.printf (
        "{ \"count\": %u, \"dnd\": %s, \"visible\": %s, \"inhibited\": %s }\n",
        count, dnd.to_string (), cc_open.to_string (), inhibited.to_string ());
    stdout.flush ();
}

private void print_subscribe () {
    try {
        SwayncDaemonData data = cc_daemon.get_subscribe_data ();
        on_subscribe (data.count, data.dnd, data.cc_open, data.inhibited);
    } catch (Error e) {
        on_subscribe (0, false, false, false);
    }
}

private void on_subscribe_waybar (uint count, bool dnd, bool cc_open, bool inhibited) {
    string state = (dnd ? "dnd-" : "")
        + (inhibited ? "inhibited-" : "")
        + (count > 0 ? "notification" : "none");

    string tooltip = "";
    if (count > 0) {
        tooltip = "%u Notification%s".printf (count, count > 1 ? "s" : "");
    }

    string _class = "\"%s\"".printf (state);
    if (cc_open) {
        _class = "[%s, \"cc-open\"]".printf (_class);
    }

    print (
        "{\"text\": \"%u\", \"alt\": \"%s\", \"tooltip\": \"%s\", \"class\": %s}\n",
        count, state, tooltip, _class);
}

private void print_subscribe_waybar () {
    try {
        SwayncDaemonData data = cc_daemon.get_subscribe_data ();
        on_subscribe_waybar (data.count, data.dnd, data.cc_open, data.inhibited);
    } catch (Error e) {
        on_subscribe_waybar (0, false, false, false);
    }
}

public int command_line (ref string[] args, bool skip_wait) {
    // Used to know how many args the current command consumed
    int used_args = 1;
    try {
        if (args.length < 1) {
            print_help (args);
            Process.exit (1);
        }
        switch (args[0]) {
            case "--help":
            case "-h":
                print_help (args);
                break;
            case "--version":
            case "-v":
                stdout.printf ("%s\n", Constants.VERSION);
                break;
            case "--reload-config":
            case "-R":
                cc_daemon.reload_config ();
                break;
            case "--reload-css":
            case "-rs":
                stdout.printf ("CSS reload success: %s\n",
                               cc_daemon.reload_css ().to_string ());
                break;
            case "--count":
            case "-c":
                print (cc_daemon.notification_count ().to_string ());
                break;
            case "--close-latest":
                cc_daemon.hide_latest_notifications (true);
                break;
            case "--hide-latest":
                cc_daemon.hide_latest_notifications (false);
                break;
            case "--hide-all":
                cc_daemon.hide_all_notifications ();
                break;
            case "--close-all":
            case "-C":
                cc_daemon.close_all_notifications ();
                break;
            case "--toggle-panel":
            case "-t":
                cc_daemon.toggle_visibility ();
                break;
            case "--open-panel":
            case "-op":
                cc_daemon.set_visibility (true);
                break;
            case "--close-panel":
            case "-cp":
                cc_daemon.set_visibility (false);
                break;
            case "--toggle-dnd":
            case "-d":
                print (cc_daemon.toggle_dnd ().to_string ());
                break;
            case "--get-dnd":
            case "-D":
                print (cc_daemon.get_dnd ().to_string ());
                break;
            case "--dnd-on":
            case "-dn":
                cc_daemon.set_dnd (true);
                print (cc_daemon.get_dnd ().to_string ());
                break;
            case "--dnd-off":
            case "-df":
                cc_daemon.set_dnd (false);
                print (cc_daemon.get_dnd ().to_string ());
                break;
            case "--action":
            case "-a":
                int action_index = 0;
                if (args.length >= 2) {
                    used_args++;
                    action_index = int.parse (args[1]);
                }
                cc_daemon.latest_invoke_action ((uint32) action_index);
                break;
            case "--get-inhibited":
            case "-I":
                print (cc_daemon.is_inhibited ().to_string ());
                break;
            case "--get-num-inhibitors":
            case "-In":
                print (cc_daemon.number_of_inhibitors ().to_string ());
                break;
            case "--inhibitor-add":
            case "-Ia":
                if (args.length < 2) {
                    stderr.printf ("Application ID needed!");
                    Process.exit (1);
                }
                used_args++;
                if (cc_daemon.add_inhibitor (args[1])) {
                    print ("Added inhibitor: \"%s\"", args[1]);
                    break;
                }
                stderr.printf ("Inhibitor: \"%s\" already added!...", args[1]);
                break;
            case "--inhibitor-remove":
            case "-Ir":
                if (args.length < 2) {
                    stderr.printf ("Application ID needed!");
                    Process.exit (1);
                }
                used_args++;
                if (cc_daemon.remove_inhibitor (args[1])) {
                    print ("Removed inhibitor: \"%s\"", args[1]);
                    break;
                }
                stderr.printf ("Inhibitor: \"%s\" does not exist!...", args[1]);
                break;
            case "inhibitors-clear":
            case "-Ic":
                if (cc_daemon.clear_inhibitors ()) {
                    print ("Cleared all inhibitors");
                    break;
                }
                print ("No inhibitors to clear...");
                break;
            case "--subscribe":
            case "-s":
                cc_daemon.subscribe_v2.connect (on_subscribe);
                var data = cc_daemon.get_subscribe_data ();
                on_subscribe (data.count,
                              data.dnd,
                              data.cc_open,
                              data.inhibited);
                var loop = new MainLoop ();
                Bus.watch_name (
                    BusType.SESSION,
                    "org.erikreider.swaync.cc",
                    BusNameWatcherFlags.NONE,
                    print_subscribe,
                    print_subscribe);
                loop.run ();
                break;
            case "--subscribe-waybar":
            case "-swb":
                cc_daemon.subscribe_v2.connect (on_subscribe_waybar);
                var loop = new MainLoop ();
                Bus.watch_name (
                    BusType.SESSION,
                    "org.erikreider.swaync.cc",
                    BusNameWatcherFlags.NONE,
                    print_subscribe_waybar,
                    print_subscribe_waybar);
                loop.run ();
                break;
            case "--change-cc-monitor":
                if (args.length < 2) {
                    stderr.printf ("Monitor connector name needed!");
                    Process.exit (1);
                }
                used_args++;
                if (cc_daemon.set_cc_monitor (args[1])) {
                    print ("Changed monitor to: \"%s\"", args[1]);
                    break;
                }
                stderr.printf ("Could not find monitor: \"%s\"!", args[1]);
                break;
            case "--change-noti-monitor":
                if (args.length < 2) {
                    stderr.printf ("Monitor connector name needed!");
                    Process.exit (1);
                }
                used_args++;
                if (cc_daemon.set_noti_window_monitor (args[1])) {
                    print ("Changed monitor to: \"%s\"", args[1]);
                    break;
                }
                stderr.printf ("Could not find monitor: \"%s\"!", args[1]);
                break;
            default:
                printerr ("Unknown command: \"%s\"\n", args[0]);
                print_help (args);
                break;
        }
    } catch (Error e) {
        stderr.printf (e.message + "\n");
        if (skip_wait) {
            Process.exit (1);
        }
        return 1;
    }

    args = args[used_args:];
    return 0;
}

void print_connection_error () {
    stderr.printf (
        "Could not connect to CC service. Will wait for connection...\n");
}

int try_connect (owned string[] args) {
    try {
        cc_daemon = Bus.get_proxy_sync (
            BusType.SESSION,
            "org.erikreider.swaync.cc",
            "/org/erikreider/swaync/cc");

        bool skip_wait = "--skip-wait" in args || "-sw" in args;
        bool one_arg = true;
        while (args.length > 0) {
            if (args[0] == "--skip-wait" || args[0] == "-sw") {
                args = args[1 :];
                continue;
            }
            if (!one_arg) {
                // Separate each command output
                print ("\n");
            }
            if (command_line (ref args, skip_wait) == 1) {
                print_connection_error ();
                return 1;
            }
            one_arg = false;
        }
        // Should only be reached if the args only contains --skip-wait or -sw
        if (one_arg) {
            stderr.printf ("Skipping wait, but with no action.");
        }
        return 0;
    } catch (Error e) {
        print_connection_error ();
        return 1;
    }
}

public int main (string[] args) {
    if (try_connect (args[1 :]) == 1) {
        MainLoop loop = new MainLoop ();
        Bus.watch_name (
            BusType.SESSION,
            "org.erikreider.swaync.cc",
            BusNameWatcherFlags.NONE,
            (conn, name, name_owner) => {
            if (try_connect (args[1 :]) == 0) {
                loop.quit ();
            }
        },
            null);
        loop.run ();
    }
    return 0;
}
0707010000005A000081A4000000000000000000000001690B16EB00000996000000000000000000000000000000000000003100000000SwayNotificationCenter-0.12.3/src/config.json.in{
  "$schema": @JSONPATH@,
  "ignore-gtk-theme": true,
  "positionX": "right",
  "positionY": "top",
  "layer": "overlay",
  "control-center-layer": "top",
  "layer-shell": true,
  "layer-shell-cover-screen": true,
  "cssPriority": "user",
  "control-center-margin-top": 0,
  "control-center-margin-bottom": 0,
  "control-center-margin-right": 0,
  "control-center-margin-left": 0,
  "notification-2fa-action": true,
  "notification-inline-replies": false,
  "notification-body-image-height": 100,
  "notification-body-image-width": 200,
  "timeout": 10,
  "timeout-low": 5,
  "timeout-critical": 0,
  "fit-to-screen": true,
  "relative-timestamps": true,
  "control-center-width": 500,
  "control-center-height": 600,
  "notification-window-width": 500,
  "keyboard-shortcuts": true,
  "notification-grouping": true,
  "image-visibility": "when-available",
  "transition-time": 200,
  "hide-on-clear": false,
  "hide-on-action": true,
  "text-empty": "No Notifications",
  "script-fail-notify": true,
  "scripts": {
    "example-script": {
      "exec": "echo 'Do something...'",
      "urgency": "Normal"
    },
    "example-action-script": {
      "exec": "echo 'Do something actionable!'",
      "urgency": "Normal",
      "run-on": "action"
    }
  },
  "notification-visibility": {
    "example-name": {
      "state": "muted",
      "urgency": "Low",
      "app-name": "Spotify"
    }
  },
  "widgets": [
    "inhibitors",
    "title",
    "dnd",
    "notifications"
  ],
  "widget-config": {
    "notifications": {
      "vexpand": true
    },
    "inhibitors": {
      "text": "Inhibitors",
      "button-text": "Clear All",
      "clear-all-button": true
    },
    "title": {
      "text": "Notifications",
      "clear-all-button": true,
      "button-text": "Clear All"
    },
    "dnd": {
      "text": "Do Not Disturb"
    },
    "label": {
      "max-lines": 5,
      "text": "Label Text"
    },
    "mpris": {
      "blacklist": [],
      "autohide": false,
      "show-album-art": "always",
      "loop-carousel": false
    },
    "buttons-grid": {
      "buttons-per-row": 7,
      "actions": [
        {
          "label": "直",
          "type": "toggle",
          "active": true,
          "command": "sh -c '[[ $SWAYNC_TOGGLE_STATE == true ]] && nmcli radio wifi on || nmcli radio wifi off'",
          "update-command": "sh -c '[[ $(nmcli radio wifi) == \"enabled\" ]] && echo true || echo false'"
        }
      ]
    }
  }
}
0707010000005B000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002E00000000SwayNotificationCenter-0.12.3/src/configModel0707010000005C000081A4000000000000000000000001690B16EB0000CC1F000000000000000000000000000000000000003F00000000SwayNotificationCenter-0.12.3/src/configModel/configModel.valanamespace SwayNotificationCenter {
    public enum PositionX {
        RIGHT, LEFT, CENTER, NONE;
    }

    public enum PositionY {
        TOP, BOTTOM, CENTER, NONE;
    }

    public enum ImageVisibility {
        ALWAYS, WHEN_AVAILABLE, NEVER;

        public string parse () {
            switch (this) {
                default:
                    return "always";
                case WHEN_AVAILABLE:
                    return "when_available";
                case NEVER:
                    return "never";
            }
        }
    }

    public enum Layer {
        BACKGROUND, BOTTOM, TOP, OVERLAY;

        public string parse () {
            switch (this) {
                case BACKGROUND:
                    return "background";
                case BOTTOM:
                    return "bottom";
                case TOP:
                    return "top";
                default:
                case OVERLAY:
                    return "overlay";
            }
        }

        public GtkLayerShell.Layer to_layer () {
            switch (this) {
                case BACKGROUND:
                    return GtkLayerShell.Layer.BACKGROUND;
                case BOTTOM:
                    return GtkLayerShell.Layer.BOTTOM;
                case TOP:
                    return GtkLayerShell.Layer.TOP;
                default:
                case OVERLAY:
                    return GtkLayerShell.Layer.OVERLAY;
            }
        }
    }

    public enum CssPriority {
        APPLICATION, USER;

        public int get_priority () {
            switch (this) {
                case APPLICATION:
                    return Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION;
                default:
                case USER:
                    return Gtk.STYLE_PROVIDER_PRIORITY_USER;
            }
        }
    }

    public class Category : Object {
        public string ?sound { get; set; default = null; }
        public string ?icon { get; set; default = null; }

        public string to_string () {
            string[] fields = {};
            if (sound != null) {
                fields += "sound: %s".printf (sound);
            }
            if (icon != null) {
                fields += "icon: %s".printf (icon);
            }
            return string.joinv (", ", fields);
        }
    }

    private struct MatchCase {
        string ?pattern;
        string ?str;

        public MatchCase (string ?pattern, string ?str) {
            this.pattern = pattern;
            this.str = str;
        }

        public bool is_valid () {
            return pattern != null && str != null;
        }
    }

    public class NotificationMatching : Object, Json.Serializable {
        public string ?app_name { get; set; default = null; }
        public string ?desktop_entry { get; set; default = null; }
        public string ?summary { get; set; default = null; }
        public string ?body { get; set; default = null; }
        public string ?urgency { get; set; default = null; }
        public string ?category { get; set; default = null; }
        public string ?sound_name { get; set; default = null; }
        public string ?sound_file { get; set; default = null; }

        private const RegexCompileFlags REGEX_COMPILE_OPTIONS =
            RegexCompileFlags.MULTILINE;

        private const RegexMatchFlags REGEX_MATCH_FLAGS = RegexMatchFlags.NOTEMPTY;

        public virtual bool matches_notification (NotifyParams param) {
            if (app_name != null) {
                if (param.app_name == null) {
                    return false;
                }
                bool result = Regex.match_simple (
                    app_name, param.app_name,
                    REGEX_COMPILE_OPTIONS,
                    REGEX_MATCH_FLAGS);
                if (!result) {
                    return false;
                }
            }
            if (desktop_entry != null) {
                if (param.desktop_entry == null) {
                    return false;
                }
                bool result = Regex.match_simple (
                    desktop_entry, param.desktop_entry,
                    REGEX_COMPILE_OPTIONS,
                    REGEX_MATCH_FLAGS);
                if (!result) {
                    return false;
                }
            }
            if (summary != null) {
                if (param.summary == null) {
                    return false;
                }
                bool result = Regex.match_simple (
                    summary, param.summary,
                    REGEX_COMPILE_OPTIONS,
                    REGEX_MATCH_FLAGS);
                if (!result) {
                    return false;
                }
            }
            if (body != null) {
                if (param.body == null) {
                    return false;
                }
                bool result = Regex.match_simple (
                    body, param.body,
                    0,
                    REGEX_MATCH_FLAGS);
                if (!result) {
                    return false;
                }
            }
            if (urgency != null) {
                bool result = Regex.match_simple (
                    urgency, param.urgency.to_string (),
                    REGEX_COMPILE_OPTIONS,
                    REGEX_MATCH_FLAGS);
                if (!result) {
                    return false;
                }
            }
            if (category != null) {
                if (param.category == null) {
                    return false;
                }
                bool result = Regex.match_simple (
                    category, param.category,
                    REGEX_COMPILE_OPTIONS,
                    REGEX_MATCH_FLAGS);
                if (!result) {
                    return false;
                }
            }
            if (sound_file != null) {
                if (param.sound_file == null) {
                    return false;
                }
                bool result = Regex.match_simple (
                    sound_file, param.sound_file,
                    REGEX_COMPILE_OPTIONS,
                    REGEX_MATCH_FLAGS);
                if (!result) {
                    return false;
                }
            }
            if (sound_name != null) {
                if (param.sound_name == null) {
                    return false;
                }
                bool result = Regex.match_simple (
                    sound_name, param.sound_name,
                    REGEX_COMPILE_OPTIONS,
                    REGEX_MATCH_FLAGS);
                if (!result) {
                    return false;
                }
            }
            return true;
        }

        public string to_string () {
            string[] fields = {};
            if (app_name != null) {
                fields += "app-name: %s".printf (app_name);
            }
            if (desktop_entry != null) {
                fields += "desktop-entry: %s".printf (desktop_entry);
            }
            if (summary != null) {
                fields += "summary: %s".printf (summary);
            }
            if (body != null) {
                fields += "body: %s".printf (body);
            }
            if (urgency != null) {
                fields += "urgency: %s".printf (urgency);
            }
            if (category != null) {
                fields += "category: %s".printf (category);
            }
            return string.joinv (", ", fields);
        }

        public override Json.Node serialize_property (string property_name,
                                                      Value value,
                                                      ParamSpec pspec) {
            // Return enum nickname instead of enum int value
            if (value.type ().is_a (Type.ENUM)) {
                var node = new Json.Node (Json.NodeType.VALUE);
                EnumClass enumc = (EnumClass) value.type ().class_ref ();
                unowned EnumValue ?eval
                    = enumc.get_value (value.get_enum ());
                if (eval == null) {
                    node.set_value (value);
                    return node;
                }
                node.set_string (eval.value_nick);
                return node;
            } else if (value.type ().is_a (Type.BOOLEAN)) {
                // Somehow the default serializer doesn't handle booleans :/
                var node = new Json.Node (Json.NodeType.VALUE);
                node.set_boolean (value.get_boolean ());
                return node;
            }
            return default_serialize_property (property_name, value, pspec);
        }
    }

    public class ActionMatching : Object, Json.Serializable {
        public string ?app_name { get; set; default = null; }
        public string ?desktop_entry { get; set; default = null; }
        public string ?id_matcher { get; set; }
        public string ?text_matcher { get; set; }
        public bool use_regex { get; set; default = false; }

        private const RegexCompileFlags REGEX_COMPILE_OPTIONS =
            RegexCompileFlags.MULTILINE;

        private const RegexMatchFlags REGEX_MATCH_FLAGS = RegexMatchFlags.NOTEMPTY;

        public bool matches_action (Action action) {
            MatchCase[] matchers = {
                MatchCase (id_matcher, action.identifier),
                MatchCase (text_matcher, action.text),
            };
            foreach (MatchCase matcher in matchers) {
                if (!matcher.is_valid ()) {
                    continue;
                }
                if (use_regex) {
                    bool result = Regex.match_simple (
                        matcher.pattern, matcher.str,
                        REGEX_COMPILE_OPTIONS,
                        REGEX_MATCH_FLAGS);
                    if (!result) {
                        return false;
                    }
                } else {
                    if (matcher.pattern != matcher.str) {
                        return false;
                    }
                }
            }

            return true;
        }

        public bool matches_notification (NotifyParams param) {
            if (id_matcher == null && text_matcher == null) {
                critical ("Action filter doesn't contain a matcher: %s", this.to_string ());
                return false;
            }

            MatchCase[] matchers = {
                MatchCase (app_name, param.app_name),
                MatchCase (desktop_entry, param.desktop_entry),
            };
            foreach (MatchCase matcher in matchers) {
                if (!matcher.is_valid ()) {
                    continue;
                }
                if (use_regex) {
                    bool result = Regex.match_simple (
                        matcher.pattern, matcher.str,
                        REGEX_COMPILE_OPTIONS,
                        REGEX_MATCH_FLAGS);
                    if (!result) {
                        return false;
                    }
                } else {
                    if (matcher.pattern != matcher.str) {
                        return false;
                    }
                }
            }

            return true;
        }

        public string to_string () {
            string[] fields = {};
            if (app_name != null) {
                fields += "app-name: %s".printf (app_name);
            }
            if (desktop_entry != null) {
                fields += "desktop-entry: %s".printf (desktop_entry);
            }
            if (id_matcher != null) {
                fields += "name_matcher: %s".printf (id_matcher);
            }
            if (text_matcher != null) {
                fields += "text_matcher: %s".printf (text_matcher);
            }
            return string.joinv (", ", fields);
        }

        public override Json.Node serialize_property (string property_name,
                                                      Value value,
                                                      ParamSpec pspec) {
            // Return enum nickname instead of enum int value
            if (value.type ().is_a (Type.ENUM)) {
                var node = new Json.Node (Json.NodeType.VALUE);
                EnumClass enumc = (EnumClass) value.type ().class_ref ();
                unowned EnumValue ?eval
                    = enumc.get_value (value.get_enum ());
                if (eval == null) {
                    node.set_value (value);
                    return node;
                }
                node.set_string (eval.value_nick);
                return node;
            } else if (value.type ().is_a (Type.BOOLEAN)) {
                // Somehow the default serializer doesn't handle booleans :/
                var node = new Json.Node (Json.NodeType.VALUE);
                node.set_boolean (value.get_boolean ());
                return node;
            }
            return default_serialize_property (property_name, value, pspec);
        }
    }

    public enum NotificationStatusEnum {
        ENABLED,
        MUTED,
        IGNORED,
        TRANSIENT;

        public string to_string () {
            switch (this) {
                default :
                return "enabled";
                case MUTED:
                    return "muted";
                case IGNORED:
                    return "ignored";
                case TRANSIENT:
                    return "transient";
            }
        }

        public static NotificationStatusEnum from_value (string value) {
            switch (value) {
                default:
                    return ENABLED;
                case "muted":
                    return MUTED;
                case "ignored":
                    return IGNORED;
                case "transient":
                    return TRANSIENT;
            }
        }
    }

    public enum NotificationUrgencyEnum {
        UNSET = -1,
        LOW = UrgencyLevels.LOW,
        NORMAL = UrgencyLevels.NORMAL,
        CRITICAL = UrgencyLevels.CRITICAL;

        public uint8 to_byte () {
            return this;
        }

        public string to_string () {
            switch (this) {
                case LOW:
                    return "Low";
                case NORMAL:
                    return "Normal";
                case CRITICAL:
                    return "Critical";
                default:
                    return "Unset";
            }
        }
    }

    public class NotificationVisibility : NotificationMatching {
        public NotificationStatusEnum state { get; set; }
        public NotificationUrgencyEnum override_urgency {
            get; set; default = NotificationUrgencyEnum.UNSET;
        }
    }

    public enum ScriptRunOnType {
        ACTION,
        RECEIVE;
    }

#if WANT_SCRIPTING
    public class Script : NotificationMatching {
        public string ?exec { get; set; default = null; }
        public ScriptRunOnType run_on { get; set; default = ScriptRunOnType.RECEIVE; }

        public async bool run_script (NotifyParams param, out string msg) {
            string[] spawn_env = {};
            spawn_env += "SWAYNC_APP_NAME=%s".printf (param.app_name);
            spawn_env += "SWAYNC_SUMMARY=%s".printf (param.summary);
            spawn_env += "SWAYNC_BODY=%s".printf (param.body);
            spawn_env += "SWAYNC_URGENCY=%s".printf (param.urgency.to_string ());
            spawn_env += "SWAYNC_CATEGORY=%s".printf (param.category);
            spawn_env += "SWAYNC_SOUND_NAME=%s".printf (param.sound_name);
            spawn_env += "SWAYNC_SOUND_FILE=%s".printf (param.sound_file);
            spawn_env += "SWAYNC_ID=%s".printf (param.applied_id.to_string ());
            spawn_env += "SWAYNC_REPLACES_ID=%s".printf (param.replaces_id.to_string ());
            spawn_env += "SWAYNC_TIME=%s".printf (param.time.to_string ());
            spawn_env += "SWAYNC_DESKTOP_ENTRY=%s".printf (param.desktop_entry ?? "");
            foreach (string hint in param.hints.get_keys ()) {
                if (hint.contains ("image") || hint.contains ("icon")) {
                    continue;
                }
                spawn_env += "SWAYNC_HINT_%s=%s".printf (
                    hint.up ().replace ("-", "_"),
                    param.hints[hint].print (false));
            }

            return yield Functions.execute_command (exec, spawn_env, out msg);
        }

        public override bool matches_notification (NotifyParams param) {
            if (exec == null) {
                return false;
            }
            return base.matches_notification (param);
        }
    }
#endif

    public class ConfigModel : Object, Json.Serializable {
        private static ConfigModel ?_instance = null;
        private static string _path = "";

        /** Get the static singleton */
        public static unowned ConfigModel instance {
            get {
                if (_instance == null) {
                    _instance = new ConfigModel ();
                }
                if (_path.length <= 0) {
                    _path = Functions.get_config_path (null);
                }
                return _instance;
            }
        }

        /** Get the static singleton and reload the config */
        public static unowned ConfigModel init (string ?path) {
            _path = path;
            reload_config ();
            return _instance;
        }

        public delegate void ModifyNode (Json.Node node);

        /** Reloads the config and calls `ModifyNode` before deserializing */
        public static void reload_config (ModifyNode modify_cb = () => {}) {
            // Re-check if config file path still exists
            string path = Functions.get_config_path (_path);
            path = File.new_for_path (path).get_path () ?? path;
            message ("Loading Config: \"%s\"", path);

            ConfigModel m = null;
            try {
                if (path.strip ().length == 0) {
                    return;
                }
                Json.Parser parser = new Json.Parser ();
                parser.load_from_file (path);
                Json.Node ?node = parser.get_root ();
                if (node == null) {
                    throw new Json.ParserError.PARSE ("Node is null!");
                }

                modify_cb (node);

                ConfigModel model = Json.gobject_deserialize (
                    typeof (ConfigModel), node) as ConfigModel;
                if (model == null) {
                    throw new Json.ParserError.UNKNOWN ("Json model is null!");
                }
                m = model;
            } catch (Error e) {
                critical (e.message);
                m = new ConfigModel ();
            }

            ConfigModel ?previous_config = _instance;

            _instance = m;
            _path = path;
            debug (_instance.to_string ());

            if (app != null) {
                app.config_reload (previous_config, m);
            }
        }

        /* Properties */

        /** Unsets the GTK_THEME env variable when true */
        public bool ignore_gtk_theme { get; set; default = true; }

        /** The notifications and controlcenters horizontal alignment */
        public PositionX positionX { // vala-lint=naming-convention
            get; set; default = PositionX.RIGHT;
        }
        /** The notifications and controlcenters vertical alignment */
        public PositionY positionY { // vala-lint=naming-convention
            get; set; default = PositionY.TOP;
        }

        /** Layer of notification window */
        public Layer layer {
            get; set; default = Layer.OVERLAY;
        }

        /**
         * Wether or not the windows should be opened as layer-shell surfaces
         */
        public bool layer_shell { get; set; default = true; }

        /**
         * Wether or not the windows should cover the whole screen when
         * layer-shell is used.
         */
        public bool layer_shell_cover_screen { get; set; default = true; }

        /** The CSS loading priority */
        public CssPriority cssPriority { // vala-lint=naming-convention
            get; set; default = CssPriority.USER;
        }

        /** The timeout for notifications with NORMAL priority */
        private const int TIMEOUT_DEFAULT = 10;
        private int _timeout = TIMEOUT_DEFAULT;
        public int timeout {
            get {
                return _timeout;
            }
            set {
                _timeout = value < 0 ? TIMEOUT_DEFAULT : value;
            }
        }

        /** The timeout for notifications with LOW priority */
        private const int TIMEOUT_LOW_DEFAULT = 5;
        private int _timeout_low = TIMEOUT_LOW_DEFAULT;
        public int timeout_low {
            get {
                return _timeout_low;
            }
            set {
                _timeout_low = value < 0 ? TIMEOUT_LOW_DEFAULT : value;
            }
        }

        /** The timeout for notifications with CRITICAL priority */
        private const int TIMEOUT_CRITICAL_DEFAULT = 0;
        private int _timeout_critical = TIMEOUT_CRITICAL_DEFAULT;
        public int timeout_critical {
            get {
                return _timeout_critical;
            }
            set {
                _timeout_critical = value < 0 ? TIMEOUT_CRITICAL_DEFAULT : value;
            }
        }

        /** The transition time for all animations */
        private const int TRANSITION_TIME_DEFAULT = 200;
        private int _transition_time = TRANSITION_TIME_DEFAULT;
        public int transition_time {
            get {
                return _transition_time;
            }
            set {
                _transition_time = value < 0 ? TRANSITION_TIME_DEFAULT : value;
            }
        }

        /*
         * Specifies if the control center should use keyboard shortcuts
         * and block keyboard input for other applications while open
         */
        public bool keyboard_shortcuts { get; set; default = true; }

        /**
         * If notifications should be grouped by app name
         */
        public bool notification_grouping { get; set; default = true; }

        /** Specifies if the notification image should be shown or not */
        public ImageVisibility image_visibility {
            get;
            set;
            default = ImageVisibility.WHEN_AVAILABLE;
        }

        /**
         * Notification window's width, in pixels.
         */
        public int notification_window_width { get; set; default = 500; }
        /** Max height of the notification in pixels */
        public int notification_window_height { get; set; default = -1; }

        /**
         * The preferred output to open the notification window (popup notifications).
         *
         * Can either be the monitor connector name (ex: "DP-1"),
         * or the full name, manufacturer model serial
         * (ex: "Acer Technologies XV272U V 503023B314202").
         *
         * If the output is not found, the currently focused one is picked.
         */
        public string notification_window_preferred_output { get; set; default = ""; }

        /** Hides the control center after clearing all notifications */
        public bool hide_on_clear { get; set; default = false; }

        /** Hides the control center when clicking on notification action */
        public bool hide_on_action { get; set; default = true; }

        /** Text that appears when there are no notifications to show */
        public string text_empty { get; set; default = "No Notifications"; }

        /** The controlcenters horizontal alignment. Supersedes `positionX` if not `NONE` */
        public PositionX control_center_positionX { // vala-lint=naming-convention
            get; set; default = PositionX.NONE;
        }
        /** The controlcenters vertical alignment. Supersedes `positionY` if not `NONE` */
        public PositionY control_center_positionY { // vala-lint=naming-convention
            get; set; default = PositionY.NONE;
        }

        /** GtkLayerShell margins around the notification center */
        private int control_center_margin_top_ = 0;
        public int control_center_margin_top {
            get {
                return control_center_margin_top_;
            }
            set {
                control_center_margin_top_ = value < 0 ? 0 : value;
            }
        }

        private int control_center_margin_bottom_ = 0;
        public int control_center_margin_bottom {
            get {
                return control_center_margin_bottom_;
            }
            set {
                control_center_margin_bottom_ = value < 0 ? 0 : value;
            }
        }

        private int control_center_margin_left_ = 0;
        public int control_center_margin_left {
            get {
                return control_center_margin_left_;
            }
            set {
                control_center_margin_left_ = value < 0 ? 0 : value;
            }
        }

        private int control_center_margin_right_ = 0;
        public int control_center_margin_right {
            get {
                return control_center_margin_right_;
            }
            set {
                control_center_margin_right_ = value < 0 ? 0 : value;
            }
        }

        /** Layer of Control Center window */
        public Layer control_center_layer {
            get; set; default = Layer.TOP;
        }

        public bool control_center_exclusive_zone {
            get; set; default = true;
        }

        /** Categories settings */
        public OrderedHashTable<Category> categories_settings {
            get;
            set;
            default = new OrderedHashTable<Category> ();
        }

        /** Filter Notification Actions */
        public OrderedHashTable<ActionMatching> notification_action_filter {
            get;
            set;
            default = new OrderedHashTable<ActionMatching> ();
        }

        /** Notification Status */
        public OrderedHashTable<NotificationVisibility> notification_visibility {
            get;
            set;
            default = new OrderedHashTable<NotificationVisibility> ();
        }

#if WANT_SCRIPTING
        /** Scripts */
        public OrderedHashTable<Script> scripts {
            get;
            set;
            default = new OrderedHashTable<Script> ();
        }

        /** Show notification if script fails */
        public bool script_fail_notify { get; set; default = true; }
#endif

        /** Whether to expand the notification center across both edges of the screen */
        public bool fit_to_screen { get; set; default = true; }

        /**
         * Display notification timestamp relative to now e.g. "26 minutes ago".
         * If false, a local iso8601-formatted absolute timestamp is displayed.
         */
        public bool relative_timestamps { get; set; default = true; }

        /**
         * Height of the control center in pixels. A value of -1 means that it
         * will fit to the content. Ignored when 'fit-to-screen' is set to 'true'.
         * Also limited to the height of the monitor, unless 'layer-shell-cover-screen'
         * is set to false.
         */
        private int _control_center_height = 500;
        public int control_center_height {
            get {
                return _control_center_height;
            }
            set {
                if (value < 1) {
                    _control_center_height = -1;
                    return;
                }
                _control_center_height = value;
            }
        }

        /**
         * Notification center's width, in pixels.
         */
        private const int CONTROL_CENTER_MINIMUM_WIDTH = 300;
        private const int CONTROL_CENTER_DEFAULT_WIDTH = 500;
        private int _control_center_width = CONTROL_CENTER_DEFAULT_WIDTH;
        public int control_center_width {
            get {
                return _control_center_width;
            }
            set {
                _control_center_width = value > CONTROL_CENTER_MINIMUM_WIDTH
                    ? value : CONTROL_CENTER_MINIMUM_WIDTH;
            }
        }

        /**
         * The preferred output to open the control center.
         *
         * Can either be the monitor connector name (ex: "DP-1"),
         * or the full name, manufacturer model serial
         * (ex: "Acer Technologies XV272U V 503023B314202").
         *
         * If the output is not found, the currently focused one is picked.
         */
        public string control_center_preferred_output { get; set; default = ""; }

        /**
         * If each notification should display a 'COPY \"1234\"' action
         */
        public bool notification_2fa_action { get; set; default = true; }

        /**
         * If notifications should display a text field to reply if the
         * sender requests it.
         */
        public bool notification_inline_replies { get; set; default = false; }

        /**
         * Notification icon size, in pixels.
         */
        [Version (deprecated = true, replacement = "CSS root variable")]
        public int notification_icon_size {
            get; set; default = -1;
        }

        /**
         * Notification body image height, in pixels.
         */
        private const int NOTIFICATION_BODY_IMAGE_MINIMUM_HEIGHT = 100;
        private const int NOTIFICATION_BODY_IMAGE_DEFAULT_HEIGHT = 100;
        private int _notification_body_image_height = NOTIFICATION_BODY_IMAGE_DEFAULT_HEIGHT;
        public int notification_body_image_height {
            get {
                return _notification_body_image_height;
            }
            set {
                _notification_body_image_height =
                    value > NOTIFICATION_BODY_IMAGE_MINIMUM_HEIGHT
                    ? value : NOTIFICATION_BODY_IMAGE_MINIMUM_HEIGHT;
            }
        }

        /**
         * Notification body image width, in pixels.
         */
        private const int NOTIFICATION_BODY_IMAGE_MINIMUM_WIDTH = 200;
        private const int NOTIFICATION_BODY_IMAGE_DEFAULT_WIDTH = 200;
        private int _notification_body_image_width = NOTIFICATION_BODY_IMAGE_DEFAULT_WIDTH;
        public int notification_body_image_width {
            get {
                return _notification_body_image_width;
            }
            set {
                _notification_body_image_width =
                    value > NOTIFICATION_BODY_IMAGE_MINIMUM_WIDTH
                    ? value : NOTIFICATION_BODY_IMAGE_MINIMUM_WIDTH;
            }
        }

        /** Widgets to show in ControlCenter */
        public GenericArray<string> widgets {
            get;
            set;
            default = new GenericArray<string> ();
        }

        /** Widgets to show in ControlCenter */
        public OrderedHashTable<Json.Object> widget_config {
            get;
            set;
            default = new OrderedHashTable<Json.Object> ();
        }


        /* Methods */

        /**
         * Selects the deserialization method based on the property name.
         * Needed to parse those parameters of complex types like hashtables,
         * which are not natively supported by the default deserialization function.
         */
        public override bool deserialize_property (string property_name,
                                                   out Value value,
                                                   ParamSpec pspec,
                                                   Json.Node property_node) {
            switch (property_name) {
                case "categories-settings" :
                    bool status;
                    OrderedHashTable<Category> result =
                        extract_hashtable<Category> (
                            property_name,
                            property_node,
                            out status);
                    value = result;
                    return status;
                case "notification-action-filter" :
                    bool status;
                    OrderedHashTable<ActionMatching> result =
                        extract_hashtable<ActionMatching> (
                            property_name,
                            property_node,
                            out status);
                    value = result;
                    return status;
                case "notification-visibility" :
                    bool status;
                    OrderedHashTable<NotificationVisibility> result =
                        extract_hashtable<NotificationVisibility> (
                            property_name,
                            property_node,
                            out status);
                    value = result;
                    return status;
#if WANT_SCRIPTING
                case "scripts":
                    bool status;
                    OrderedHashTable<Script> result =
                        extract_hashtable<Script> (
                            property_name,
                            property_node,
                            out status);
                    value = result;
                    return status;
#endif
                case "widgets":
                    bool status;
                    GenericArray<string> result =
                        extract_array<string> (property_name,
                                               property_node,
                                               out status);
                    value = result;
                    return status;
                case "widget-config":
                    OrderedHashTable<Json.Object> result
                        = new OrderedHashTable<Json.Object> ();
                    if (property_node.get_value_type ().name () != "JsonObject") {
                        value = result;
                        return true;
                    }
                    Json.Object obj = property_node.get_object ();
                    if (obj.get_size () == 0) {
                        value = result;
                        return true;
                    }
                    foreach (var key in obj.get_members ()) {
                        Json.Node ?node = obj.get_member (key);
                        if (node.get_node_type () != Json.NodeType.OBJECT) {
                            continue;
                        }
                        Json.Object ?o = node.get_object ();
                        if (o != null) {
                            result.insert (key, o);
                        }
                    }
                    value = result;
                    return true;
                    default :
                    // Handles all other properties
                    return default_deserialize_property (
                        property_name, out value, pspec, property_node);
            }
        }

        /**
         * Called when `Json.gobject_serialize (ConfigModel.instance)` is called
         */
        public Json.Node serialize_property (string property_name,
                                             Value value,
                                             ParamSpec pspec) {
            var node = new Json.Node (Json.NodeType.VALUE);
            if (value.type ().is_a (Type.ENUM)) {
                EnumClass enumc = (EnumClass) pspec.value_type.class_ref ();
                EnumValue ?eval
                    = enumc.get_value (value.get_enum ());
                if (eval == null) {
                    node.set_value (value);
                    return node;
                }
                node.set_string (eval.value_nick);
                return node;
            }
            // All other properties that can't be serialized
            switch (property_name) {
                case "categories-settings" :
                    node = new Json.Node (Json.NodeType.OBJECT);
                    var table = (OrderedHashTable<Category>) value;
                    node.set_object (serialize_hashtable<Category> (table));
                    break;
                case "notification-action-filter" :
                    node = new Json.Node (Json.NodeType.OBJECT);
                    var table = (OrderedHashTable<ActionMatching>) value;
                    node.set_object (serialize_hashtable<ActionMatching> (table));
                    break;
                case "notification-visibility":
                    node = new Json.Node (Json.NodeType.OBJECT);
                    var table = (OrderedHashTable<NotificationVisibility>) value;
                    node.set_object (serialize_hashtable<NotificationVisibility> (table));
                    break;
#if WANT_SCRIPTING
                case "scripts":
                    node = new Json.Node (Json.NodeType.OBJECT);
                    var table = (OrderedHashTable<Script>) value;
                    node.set_object (serialize_hashtable<Script> (table));
                    break;
#endif
                case "widgets":
                    node = new Json.Node (Json.NodeType.ARRAY);
                    var table = (GenericArray<string>) value;
                    node.set_array (serialize_array<string> (table));
                    break;
                case "widget-config":
                    node = new Json.Node (Json.NodeType.OBJECT);
                    var table = (OrderedHashTable<Json.Object>) value;
                    node.set_object (serialize_hashtable<Json.Object> (table));
                    break;
                default:
                    node.set_value (value);
                    break;
            }
            return node;
        }

        public string to_string () {
            var json = Json.gobject_serialize (ConfigModel.instance);
            return Json.to_string (json, true);
        }

        /**
         * Extracts and returns a OrderedHashTable<GLib.Object>
         * from a nested JSON Object.
         *
         * Can only accept these types:
         * - string
         * - bool
         * - int64
         * - GLib.Object
         */
        private OrderedHashTable<T> extract_hashtable<T> (string property_name,
                                                          Json.Node node,
                                                          out bool status) {
            status = false;
            var tmp_table = new OrderedHashTable<T> ();

            if (node.get_node_type () != Json.NodeType.OBJECT) {
                stderr.printf ("Node %s is not a json object!...\n",
                               property_name);
                return tmp_table;
            }

            Json.Object ?root_object = node.get_object ();
            if (root_object == null) {
                return tmp_table;
            }

            Type generic_type = Functions.get_base_type (typeof (T));
            foreach (string *key in root_object.get_members ()) {
                unowned Json.Node ?member = root_object.get_member (key);
                if (member == null) {
                    continue;
                }

                if (!member.get_value_type ().is_a (generic_type)
                    && !member.get_value_type ().is_a (typeof (Json.Object))) {
                    continue;
                }

                switch (generic_type) {
                    case Type.STRING :
                        unowned string ?str = member.get_string ();
                        if (str != null) {
                            tmp_table.insert (key, str);
                        }
                        break;
                    case Type.BOOLEAN :
                        tmp_table.insert (key, member.get_boolean ());
                        break;
                    case Type.INT64:
                        tmp_table.insert (key, (int64 ?) member.get_int ());
                        break;
                    case Type.OBJECT :
                        if (!typeof (T).is_a (Type.OBJECT)) {
                            break;
                        }

                        unowned Json.Object ?object =
                            root_object.get_object_member (key);
                        if (object == null) {
                            break;
                        }

                        // Creates a new GLib.Object with all of the properties of T
                        Type type = typeof (T);
                        ObjectClass ocl = (ObjectClass) type.class_ref ();
                        Object obj = Object.new (type);
                        foreach (var name in object.get_members ()) {
                            Value value = object.get_member (name).get_value ();

                            unowned ParamSpec value_spec = null;
                            foreach (var spec in ocl.list_properties ()) {
                                if (spec.name == name) {
                                    value_spec = spec;
                                    break;
                                }
                            }
                            if (value_spec == null) {
                                continue;
                            }

                            unowned Type spec_type = value_spec.value_type;
                            unowned Type val_type = value.type ();
                            if (spec_type.is_a (val_type)) {
                                // Both are the same type
                                obj.set_property (name, value);
                            } else if (spec_type.is_a (Type.ENUM)
                                       && val_type.is_a (Type.STRING)) {
                                // Set enum from string
                                EnumClass enumc = (EnumClass) spec_type.class_ref ();
                                EnumValue ?eval
                                    = enumc.get_value_by_nick (value.get_string ());
                                if (eval != null) {
                                    obj.set_property (name, eval.value);
                                }
                            }
                        }

                        tmp_table.insert (key, (T) obj);
                        break;
                }
            }

            status = true;
            return tmp_table;
        }

        private Json.Object serialize_hashtable<T> (OrderedHashTable<T> table) {
            var json_object = new Json.Object ();

            if (table == null) {
                return json_object;
            }

            foreach (string key in table.get_keys ()) {
                unowned T item = table.get (key);
                if (item == null) {
                    continue;
                }

                Type generic_type = Functions.get_base_type (typeof (T));
                switch (generic_type) {
                    case Type.STRING :
                        string ?casted = (string) item;
                        if (casted != null) {
                            json_object.set_string_member (key, casted);
                        }
                        break;
                    case Type.BOOLEAN :
                        bool ?casted = (bool) item;
                        if (casted != null) {
                            json_object.set_boolean_member (key, casted);
                        }
                        break;
                    case Type.INT64:
                        int64 ?casted = (int64 ?) item;
                        if (casted != null) {
                            json_object.set_int_member (key, casted);
                        }
                        break;
                    case Type.OBJECT :
                        var node = Json.gobject_serialize (item as Object);
                        json_object.set_member (key, node);
                        break;
                    case Type.BOXED :
                        switch (typeof (T).name ()) {
                            case "JsonObject":
                                json_object.set_object_member (key,
                                                               (Json.Object) item);
                                break;
                            case "JsonArray":
                                json_object.set_array_member (key,
                                                              (Json.Array) item);
                                break;
                        }
                        break;
                }
            }
            return json_object;
        }

        /**
         * Extracts a JSON array and returns a GLib.GenericArray<T>
         *
         * Can only accept these types:
         * - string
         * - bool
         * - int64
         * - GLib.Object
         */
        private GenericArray<T> extract_array<T> (string property_name,
                                                  Json.Node node,
                                                  out bool status) {
            status = false;
            GenericArray<T> tmp_array = new GenericArray<T> ();

            if (node.get_node_type () != Json.NodeType.ARRAY) {
                stderr.printf ("Node %s is not a json array!...\n",
                               property_name);
                return tmp_array;
            }

            Json.Array ?root_array = node.get_array ();
            if (root_array == null) {
                return tmp_array;
            }

            foreach (Json.Node * member in root_array.get_elements ()) {
                Type generic_type = Functions.get_base_type (typeof (T));
                if (!member->get_value_type ().is_a (generic_type)
                    && !member->get_value_type ().is_a (typeof (Json.Object))) {
                    continue;
                }

                switch (generic_type) {
                    case Type.STRING :
                        unowned string ?str = member->get_string ();
                        if (str != null) {
                            tmp_array.add (str);
                        }
                        break;
                    case Type.BOOLEAN:
                        tmp_array.add (member->get_boolean ());
                        break;
                    case Type.INT64:
                        tmp_array.add (member->get_int ());
                        break;
                    case Type.OBJECT:
                        if (!typeof (T).is_a (Type.OBJECT)) {
                            break;
                        }

                        unowned Json.Object ?object = member->get_object ();
                        if (object == null) {
                            break;
                        }

                        // Creates a new GLib.Object with all of the properties of T
                        Object obj = Object.new (typeof (T));
                        foreach (var name in object.get_members ()) {
                            Value value = object.get_member (name).get_value ();
                            obj.set_property (name, value);
                        }

                        tmp_array.add ((T) obj);
                        break;
                }
            }

            status = true;
            return tmp_array;
        }

        private Json.Array serialize_array<T> (GenericArray<T> array) {
            var json_array = new Json.Array ();

            if (array == null) {
                return json_array;
            }

            foreach (T item in array.data) {
                if (item == null) {
                    continue;
                }
                Type generic_type = Functions.get_base_type (typeof (T));
                switch (generic_type) {
                    case Type.STRING :
                        string ?casted = (string) item;
                        if (casted != null) {
                            json_array.add_string_element (casted);
                        }
                        break;
                    case Type.BOOLEAN:
                        bool ?casted = (bool) item;
                        if (casted != null) {
                            json_array.add_boolean_element (casted);
                        }
                        break;
                    case Type.INT64:
                        int64 ?casted = (int64) item;
                        if (casted != null) {
                            json_array.add_int_element (casted);
                        }
                        break;
                    case Type.OBJECT :
                        var node = Json.gobject_serialize (item as Object);
                        json_array.add_element (node);
                        break;
                }
            }
            return json_array;
        }

        /**
         * Changes the `member_name` to the specified value if their types match
         */
        public void change_value (string member_name,
                                  Variant value,
                                  bool write = true,
                                  string ?path = null) {
            reload_config ((node) => {
                unowned Json.Object obj = node.get_object ();
                if (obj == null) {
                    return;
                }
                debug ("Config change: %s %s",
                       member_name, value.get_type_string ());
                switch (value.get_type_string ()) {
                    case "i":
                        int val = value.get_int32 ();
                        obj.set_int_member (member_name, val);
                        debug ("Config changed %s", member_name);
                        break;
                    case "s":
                        string val = value.get_string ();
                        obj.set_string_member (member_name, val);
                        debug ("Config changed %s", member_name);
                        break;
                    case "b":
                        bool val = value.get_boolean ();
                        obj.set_boolean_member (member_name, val);
                        debug ("Config changed %s", member_name);
                        break;
                }
            });

            if (!write) {
                debug ("Skipped writing new config to %s", path);
                return;
            }
            if (write_to_file (path)) {
                debug ("Successfully wrote to %s", path);
            } else {
                critical ("ERROR WRITING TO %s", path);
            }
        }

        /**
         * Writes and replaces settings with the new settings in `path`. If
         * `path` is "null", the default user accessible config will be used
         * ("~/.config/swaync/config.json")
         */
        private bool write_to_file (owned string ?path = null) {
            try {
                if (path == null) {
                    // Use the default user accessible config
                    string dir_path = Path.build_path (
                        Path.DIR_SEPARATOR.to_string (),
                        Environment.get_user_config_dir (),
                        "swaync");
                    path = Path.build_path (Path.DIR_SEPARATOR.to_string (),
                                            dir_path, "config.json");
                    var dir = File.new_for_path (dir_path);
                    if (!dir.query_exists ()) {
                        dir.make_directory ();
                    }
                    var file = File.new_for_path (path);
                    if (!file.query_exists ()) {
                        file.create (FileCreateFlags.NONE);
                    }
                }

                var file = File.new_for_path (path);

                string data = ConfigModel.instance.to_string ();
                return file.replace_contents (
                    data.data,
                    null,
                    false,
                    FileCreateFlags.REPLACE_DESTINATION,
                    null);
            } catch (Error e) {
                stderr.printf (e.message + "\n");
                return false;
            }
        }
    }
}
0707010000005D000081A4000000000000000000000001690B16EB00007163000000000000000000000000000000000000003400000000SwayNotificationCenter-0.12.3/src/configSchema.json{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "SwayNotificationCenter JSON schema",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "$schema": {
      "type": "string",
      "description": "Pointer to the schema against which this document should be validated."
    },
    "ignore-gtk-theme": {
      "type": "boolean",
      "description": "Unsets the GTK_THEME environment variable, fixing a lot of issues with GTK themes ruining the users custom CSS themes.",
      "default": true
    },
    "positionX": {
      "type": "string",
      "description": "Horizontal position of control center and notification window",
      "default": "right",
      "enum": ["right", "left", "center"]
    },
    "layer": {
      "type": "string",
      "description": "Layer of notification window",
      "default": "overlay",
      "enum": ["background", "bottom", "top", "overlay"]
    },
    "layer-shell": {
      "type": "boolean",
      "description": "Whether or not the windows should be opened as layer-shell surfaces. Note: Requires swaync restart to apply",
      "default": true
    },
    "layer-shell-cover-screen": {
      "type": "boolean",
      "description": "Whether or not the windows should cover the whole screen when layer-shell is used. May exceed the height of the monitor if the value is too large. Fixes animations in compositors like Hyprland.",
      "default": true
    },
    "cssPriority": {
      "type": "string",
      "description": "Which GTK priority to use when loading the default and user CSS files. Pick \"user\" to override XDG_CONFIG_HOME/gtk-4.0/gtk.css",
      "default": "highest",
      "enum": ["application", "user"]
    },
    "positionY": {
      "type": "string",
      "description": "Vertical position of control center and notification window",
      "default": "top",
      "enum": ["top", "center", "bottom"]
    },
    "control-center-positionX": {
      "type": "string",
      "description": "Optional: Horizontal position of the control center. Supersedes positionX if not set to `none`",
      "default": "none",
      "enum": ["right", "left", "center", "none"]
    },
    "control-center-positionY": {
      "type": "string",
      "description": "Optional: Vertical position of the control center. Supersedes positionY if not set to `none`",
      "default": "none",
      "enum": ["top", "bottom", "none"]
    },
    "control-center-margin-top": {
      "type": "integer",
      "description": "The margin (in pixels) at the top of the control center. 0 to disable",
      "default": 0
    },
    "control-center-margin-bottom": {
      "type": "integer",
      "description": "The margin (in pixels) at the bottom of the control center. 0 to disable",
      "default": 0
    },
    "control-center-margin-right": {
      "type": "integer",
      "description": "The margin (in pixels) at the right of the control center. 0 to disable",
      "default": 0
    },
    "control-center-margin-left": {
      "type": "integer",
      "description": "The margin (in pixels) at the left of the control center. 0 to disable",
      "default": 0
    },
    "control-center-layer": {
      "type": "string",
      "description": "Layer of control center window",
      "default": "none",
      "enum": ["background", "bottom", "top", "overlay", "none"]
    },
    "control-center-exclusive-zone": {
      "type": "boolean",
      "description": "Whether or not the control center should follow the compositors exclusive zones. An example would be setting it to \"false\" to cover your panel/dock.",
      "default": true
    },
    "notification-2fa-action": {
      "type": "boolean",
      "description": "If each notification should display a 'COPY \"1234\"' action",
      "default": true
    },
    "notification-inline-replies": {
      "type": "boolean",
      "description": "If notifications should display a text field to reply if the sender requests it. NOTE: Replying in popup notifications is only available if the compositor supports GTK Layer-Shell ON_DEMAND keyboard interactivity.",
      "default": false
    },
    "notification-icon-size": {
      "type": "integer",
      "deprecated": true,
      "description": "deprecated (change the CSS root variable \"--notification-icon-size\"): The notification icon size (in pixels). The app icon size is 1/3",
      "default": -1
    },
    "notification-body-image-height": {
      "type": "integer",
      "description": "The notification body image height (in pixels)",
      "default": 100,
      "minimum": 100
    },
    "notification-body-image-width": {
      "type": "integer",
      "description": "The notification body image width (in pixels)",
      "default": 200,
      "minimum": 200
    },
    "timeout": {
      "type": "integer",
      "description": "The notification timeout for notifications with normal priority",
      "default": 10
    },
    "timeout-low": {
      "type": "integer",
      "description": "The notification timeout for notifications with low priority",
      "default": 5
    },
    "timeout-critical": {
      "type": "integer",
      "description": "The notification timeout for notifications with critical priority. 0 to disable",
      "default": 0
    },
    "notification-window-width": {
      "type": "integer",
      "description": "Width of the notification in pixels",
      "default": 500
    },
    "notification-window-height": {
      "type": "integer",
      "description": "Max height of the notification in pixels. -1 to use the full amount of space given by the compositor.",
      "default": -1
    },
    "notification-window-preferred-output": {
      "type": "string",
      "description": "The preferred output to open the notification window (popup notifications). Can either be the monitor connector name (ex: \"DP-1\"), or the full name, manufacturer model serial (ex: \"Acer Technologies XV272U V 503023B314202\"). If the output is not found, the currently focused one is picked"
    },
    "fit-to-screen": {
      "type": "boolean",
      "description": "If the control center should expand to both edges of the screen",
      "default": true
    },
    "relative-timestamps": {
      "type": "boolean",
      "description": "Display notification timestamps relative to now e.g. \"26 minutes ago\". If false, a local iso8601-formatted absolute timestamp is displayed.",
      "default": true
    },
    "control-center-height": {
      "type": "integer",
      "description": "Height of the control center in pixels. A value of -1 means that it will fit to the content. Ignored when 'fit-to-screen' is set to 'true'. Also limited to the height of the monitor, unless 'layer-shell-cover-screen' is set to false.",
      "default": 500,
      "minimum": -1
    },
    "control-center-width": {
      "type": "integer",
      "description": "Width of the control center in pixels",
      "default": 500,
      "minimum": 300
    },
    "control-center-preferred-output": {
      "type": "string",
      "description": "The preferred output to open the control center. Can either be the monitor connector name (ex: \"DP-1\"), or the full name, manufacturer model serial (ex: \"Acer Technologies XV272U V 503023B314202\"). If the output is not found, the currently focused one is picked"
    },
    "keyboard-shortcuts": {
      "type": "boolean",
      "description": "If control center should use keyboard shortcuts",
      "default": true
    },
    "notification-grouping": {
      "type": "boolean",
      "description": "If notifications should be grouped by app name",
      "default": true
    },
    "image-visibility": {
      "type": "string",
      "description": "The notification image visibility when no icon is available.",
      "default": "when-available",
      "enum": ["always", "when-available", "never"]
    },
    "transition-time": {
      "type": "integer",
      "description": "The notification animation duration. 0 to disable",
      "default": 200
    },
    "hide-on-clear": {
      "type": "boolean",
      "description": "Hides the control center after pressing \"Clear All\"",
      "default": false
    },
    "hide-on-action": {
      "type": "boolean",
      "description": "Hides the control center when clicking on notification action",
      "default": true
    },
    "text-empty": {
      "type": "string",
      "description": "Text that appears when there are no notifications to show",
      "default": "No Notifications"
    },
    "script-fail-notify": {
      "type": "boolean",
      "description": "Sends a notification if a script fails to run",
      "default": true
    },
    "scripts": {
      "type": "object",
      "description": "Which scripts to check and potentially run for every notification. If the notification doesn't include one of the properties, that property will be ignored. All properties (except for exec) use regex. If all properties match the given notification, the script will be run. Only the first matching script will be run.",
      "minProperties": 1,
      "additionalProperties": false,
      "patternProperties": {
        "^.{1,}$": {
          "type": "object",
          "description": "Your script object.",
          "required": ["exec"],
          "minProperties": 2,
          "additionalProperties": false,
          "properties": {
            "exec": {
              "type": "string",
              "description": "The script to run. Can also run regular shell commands."
            },
            "app-name": {
              "type": "string",
              "description": "The app-name. Uses Regex."
            },
            "desktop-entry": {
              "type": "string",
              "description": "The desktop-entry. Uses Regex."
            },
            "summary": {
              "type": "string",
              "description": "The summary of the notification. Uses Regex."
            },
            "body": {
              "type": "string",
              "description": "The body of the notification. Uses Regex."
            },
            "urgency": {
              "type": "string",
              "description": "The urgency of the notification.",
              "default": "Normal",
              "enum": ["Low", "Normal", "Critical"]
            },
            "category": {
              "type": "string",
              "description": "Which category the notification belongs to. Uses Regex."
            },
            "sound-file": {
              "type": "string",
              "description": "Which sound file the notification requested. Uses Regex."
            },
            "sound-name": {
              "type": "string",
              "description": "Which sound name the notification requested. Uses Regex."
            },
            "run-on": {
              "type": "string",
              "description": "Whether to run the script on an action being activated, or when the notification is received.",
              "enum": ["action", "receive"],
              "default": "receive"
            }
          }
        }
      }
    },
    "notification-action-filter": {
      "type": "object",
      "description": "Hides matching action(s) of matching notifications. If the notification doesn't include one of the properties, that property will be ignored. If all properties match the given notification, the matching actions will be hidden.",
      "minProperties": 1,
      "additionalProperties": false,
      "patternProperties": {
        "^.{1,}$": {
          "type": "object",
          "description": "Your script object.",
          "minProperties": 1,
          "anyOf": [
            { "required": ["id-matcher"] },
            { "required": ["text-matcher"] }
          ],
          "additionalProperties": false,
          "properties": {
            "use-regex": {
              "type": "boolean",
              "default": false,
              "description": "Indicates if all the below fields should use regex or not."
            },
            "app-name": {
              "type": "string",
              "description": "The app-name."
            },
            "desktop-entry": {
              "type": "string",
              "description": "The desktop-entry."
            },
            "id-matcher": {
              "type": "string",
              "description": "Matches the action identifier. Can be found by reading the output of swaync when run with the `G_MESSAGES_DEBUG=all` environment variable."
            },
            "text-matcher": {
              "type": "string",
              "description": "Matches the actions text visible in the notification."
            }
          }
        }
      }
    },
    "notification-visibility": {
      "type": "object",
      "description": "Set the visibility of each incoming notification. If the notification doesn't include one of the properties, that property will be ignored. All properties (except for state) use regex. If all properties match the given notification, the notification will be follow the provided state. Only the first matching object will be used.",
      "minProperties": 1,
      "additionalProperties": false,
      "patternProperties": {
        "^.{1,}$": {
          "type": "object",
          "description": "Your script object.",
          "required": ["state"],
          "minProperties": 2,
          "additionalProperties": false,
          "properties": {
            "state": {
              "type": "string",
              "description": "The notification visibility state.",
              "default": "enabled",
              "enum": ["ignored", "muted", "enabled", "transient"]
            },
            "app-name": {
              "type": "string",
              "description": "The app-name. Uses Regex."
            },
            "desktop-entry": {
              "type": "string",
              "description": "The desktop-entry. Uses Regex."
            },
            "summary": {
              "type": "string",
              "description": "The summary of the notification. Uses Regex."
            },
            "body": {
              "type": "string",
              "description": "The body of the notification. Uses Regex."
            },
            "urgency": {
              "type": "string",
              "description": "The urgency of the notification.",
              "default": "Normal",
              "enum": ["Low", "Normal", "Critical"]
            },
            "override-urgency": {
              "type": "string",
              "description": "The new urgency of the notification (optional)",
              "default": "unset",
              "enum": ["unset", "low", "normal", "critical"]
            },
            "category": {
              "type": "string",
              "description": "Which category the notification belongs to. Uses Regex."
            }
          }
        }
      }
    },
    "widgets": {
      "type": "array",
      "uniqueItems": true,
      "description": "Which order and which widgets to display. If the \"notifications\" widget isn't specified, it will be placed at the bottom.",
      "default": ["inhibitors", "title", "dnd", "notifications"],
      "items": {
        "type": "string",
        "$comment": "Sadly can't use regex and enums at the same time. Fix in the future?",
        "pattern": "^[a-zA-Z0-9_-]{1,}(#[a-zA-Z0-9_-]{1,}){0,1}?$"
      }
    },
    "widget-config": {
      "type": "object",
      "description": "Configure specific widget properties.",
      "additionalProperties": false,
      "$comment": "New widgets go here in \"patternProperties\" ↓",
      "patternProperties": {
        "notifications": {
          "$comment": "References the widget structure from \"widgets\" below",
          "$ref": "#/widgets/notifications"
        },
        "^title(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
          "$comment": "References the widget structure from \"widgets\" below",
          "$ref": "#/widgets/title"
        },
        "^dnd(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
          "$ref": "#/widgets/dnd"
        },
        "^label(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
          "$ref": "#/widgets/label"
        },
        "^mpris(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
          "$ref": "#/widgets/mpris"
        },
        "^buttons-grid(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
          "$ref": "#/widgets/buttons-grid"
        },
        "^menubar(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
          "$ref": "#/widgets/menubar"
        },
        "^slider(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
          "$ref": "#/widgets/slider"
        },
        "^volume(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
          "$ref": "#/widgets/volume"
        },
        "^backlight(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
          "$ref": "#/widgets/backlight"
        },
        "^inhibitors(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
          "$comment": "References the widget structure from \"widgets\" below",
          "$ref": "#/widgets/inhibitors"
        }
      }
    }
  },
  "widgets": {
    "$comment": "New widgets go here",
    "notifications": {
      "type": "object",
      "description": "Control Center Title Widget",
      "additionalProperties": false,
      "properties": {
        "vexpand": {
          "type": "boolean",
          "description": "Whether or not the notifications widget should vertically expand or not",
          "default": true
        }
      }
    },
    "title": {
      "type": "object",
      "description": "Control Center Title Widget",
      "additionalProperties": false,
      "properties": {
        "text": {
          "type": "string",
          "description": "The title of the widget",
          "default": "Notifications"
        },
        "clear-all-button": {
          "type": "boolean",
          "description": "Whether to display a \"Clear All\" button",
          "default": true
        },
        "button-text": {
          "type": "string",
          "description": "\"Clear All\" button text",
          "default": "Clear All"
        }
      }
    },
    "dnd": {
      "type": "object",
      "description": "Control Center Do Not Disturb Widget",
      "additionalProperties": false,
      "properties": {
        "text": {
          "type": "string",
          "description": "The title of the widget",
          "default": "Do Not Disturb"
        }
      }
    },
    "label": {
      "type": "object",
      "description": "A generic widget that allows the user to add custom text",
      "additionalProperties": false,
      "properties": {
        "text": {
          "type": "string",
          "description": "The text content of the widget",
          "default": "Label Text"
        },
        "max-lines": {
          "type": "integer",
          "description": "The maximum lines",
          "default": 5
        }
      }
    },
    "mpris": {
      "type": "object",
      "description": "A widget that displays multiple music players",
      "additionalProperties": false,
      "properties": {
        "image-size": {
          "type": "integer",
          "deprecated": true,
          "description": "deprecated (change the CSS root variable \"--mpris-album-art-icon-size\"): The size of the album art",
          "default": -1
        },
        "show-album-art": {
          "type": "string",
          "description": "Whether or not the album art should be hidden, always visible, or only visible when a valid album art is provided.",
          "default": "always",
          "enum": ["always", "when-available", "never"]
        },
        "autohide": {
          "type": "boolean",
          "description": "Whether to hide the widget when the player has no metadata.",
          "default": false
        },
        "blacklist": {
          "type": "array",
          "description": "Audio sources for the mpris widget to ignore.",
          "items": {
            "type": "string",
            "description": "Audio source/app name. Regex allowed."
          }
        },
        "loop-carousel": {
          "type": "boolean",
          "description": "Whether to loop through the mpris carousel.",
          "default": "false"
        }
      }
    },
    "buttons-grid": {
      "type": "object",
      "description": "A widget to add a grid of buttons that execute shell commands",
      "additionalProperties": false,
      "properties": {
        "buttons-per-row": {
          "type": "number",
          "description": "How many buttons should be shown in a buttons-grid row"
        },
        "actions": {
          "type": "array",
          "description": "A list of actions containing a label and a command",
          "items": {
            "type": "object",
            "properties": {
              "label": {
                "type": "string",
                "description": "Text to be displayed in button",
                "default": "label"
              },
              "command": {
                "type": "string",
                "description": "Command to be executed on click",
                "default": ""
              },
              "type": {
                "type": "string",
                "description": "Type of the button; toggle buttons receive the .active css class and an env variable 'SWAYNC_TOGGLE_STATE' is set. See example in the default config.json",
                "default": "normal",
                "enum": ["normal", "toggle"]
              },
              "update-command": {
                "type": "string",
                "description": "Command to be executed on visibility change of cc to update the active state of the toggle button (should echo true or false)",
                "default": ""
              },
              "active": {
                "type": "boolean",
                "description": "Whether the toggle button is active as default or not",
                "default": false
              }
            }
          }
        }
      }
    },
    "menubar": {
      "type": "object",
      "description": "A bar that contains action-buttons and buttons to open a dropdown with action-buttons",
      "additionalProperties": false,
      "patternProperties": {
        "^menu(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
          "type": "object",
          "description": "A button that opens a dropdown with action-buttons",
          "additionalProperties": false,
          "properties": {
            "label": {
              "type": "string",
              "description": "Text to be displayed in button",
              "default": "Menu"
            },
            "position": {
              "type": "string",
              "description": "Horizontal position of the button in the bar",
              "default": "right",
              "enum": ["right", "left"]
            },
            "animation-type": {
              "type": "string",
              "default": "slide_down",
              "description": "Animation type for menu",
              "enum": ["slide_down", "slide_up", "none"]
            },
            "animation-duration": {
              "type": "integer",
              "default": 250,
              "description": "Duration of animation in milliseconds"
            },
            "actions": {
              "$ref": "#/widgets/buttons-grid/properties/actions"
            }
          }
        },
        "^buttons(#[a-zA-Z0-9_-]{1,}){0,1}?$": {
          "type": "object",
          "description": "A list of action-buttons to be displayed in the topbar",
          "additionalProperties": false,
          "properties": {
            "position": {
              "type": "string",
              "description": "Horizontal position of the button in the bar",
              "default": "right",
              "enum": ["right", "left"]
            },
            "actions": {
              "$ref": "#/widgets/buttons-grid/properties/actions"
            }
          }
        }
      }
    },
    "slider": {
      "type": "object",
      "description": "general slider control",
      "additionalProperties": false,
      "properties": {
        "label": {
          "type": "string",
          "description": "Text displayed in front of the slider",
          "default": "slider"
        },
        "cmd_setter": {
          "type": "string",
          "description": "command to set the value. Use $value to get the current value",
          "default": ""
        },
        "cmd_getter": {
          "type": "string",
          "description": "command to get the actual value. Use $value to get the current value",
          "default": ""
        },
        "min": {
          "type": "integer",
          "description": "minimum value of the slider range",
          "default": 0
        },
        "max": {
          "type": "integer",
          "description": "maximum value of the slider range",
          "default": 100
        },
        "min_limit": {
          "type": "integer",
          "description": "limit minimum value of the slider",
          "default": 0
        },
        "max_limit": {
          "type": "integer",
          "description": "limit maximum value of the slider",
          "default": 100
        },
        "value_scale": {
          "type": "integer",
          "default": 0,
          "description": "scale small value, slider round digits"
        }
      }
    },
    "volume": {
      "type": "object",
      "description": "Slider to control pulse volume",
      "additionalProperties": false,
      "properties": {
        "label": {
          "type": "string",
          "description": "Text displayed in front of the volume slider",
          "default": "Volume"
        },
        "show-per-app": {
          "type": "boolean",
          "default": false,
          "description": "Show per app volume control"
        },
        "show-per-app-icon": {
          "type": "boolean",
          "default": true,
          "description": "Show application icon in per app control"
        },
        "show-per-app-label": {
          "type": "boolean",
          "default": false,
          "description": "Show application name in per app control"
        },
        "expand-per-app": {
          "type": "boolean",
          "default": false,
          "description": "If the per app section should start expanded"
        },
        "empty-list-label": {
          "type": "string",
          "default": "No active sink input",
          "description": "Text displayed when there are not active sink inputs"
        },
        "expand-button-label": {
          "type": "string",
          "default": "⇧",
          "description": "Label displayed on button to show per app volume control"
        },
        "collapse-button-label": {
          "type": "string",
          "default": "⇩",
          "description": "Label displayed on button to hide per app volume control"
        },
        "icon-size": {
          "type": "integer",
          "deprecated": true,
          "default": -1,
          "description": "deprecated (change the CSS root variable \"--widget-volume-row-icon-size\"): Size of the application icon in per app volume control"
        },
        "animation-type": {
          "type": "string",
          "default": "slide_down",
          "description": "Animation type for menu",
          "enum": ["slide_down", "slide_up", "none"]
        },
        "animation-duration": {
          "type": "integer",
          "default": 250,
          "description": "Duration of animation in milliseconds"
        }
      }
    },
    "backlight": {
      "type": "object",
      "description": "Slider to control monitor brightness",
      "additionalProperties": false,
      "properties": {
        "label": {
          "type": "string",
          "description": "Text displayed in front of the backlight slider",
          "default": "Brightness"
        },
        "device": {
          "type": "string",
          "description": "Name of monitor (find possible devices using `ls /sys/class/backlight` or `ls /sys/class/leds`)",
          "default": "intel_backlight"
        },
        "subsystem": {
          "type": "string",
          "description": "Kernel subsystem for brightness control",
          "default": "backlight",
          "enum": ["backlight", "leds"]
        },
        "min": {
          "type": "integer",
          "default": 0,
          "description": "Lowest possible value for brightness"
        }
      }
    },
    "inhibitors": {
      "type": "object",
      "description": "Control Center Inhibitors Widget",
      "additionalProperties": false,
      "properties": {
        "text": {
          "type": "string",
          "description": "The title of the widget",
          "default": "Inhibitors"
        },
        "clear-all-button": {
          "type": "boolean",
          "description": "Whether to display a \"Clear All\" button",
          "default": true
        },
        "button-text": {
          "type": "string",
          "description": "\"Clear All\" button text",
          "default": "Clear All"
        }
      }
    }
  }
}
0707010000005E000081A4000000000000000000000001690B16EB000000AC000000000000000000000000000000000000003400000000SwayNotificationCenter-0.12.3/src/constants.vala.inpublic class Constants {
    public const string VERSION = @VERSION@;
    public const string VERSIONNUM = @VERSION_NUM@;
    public const uint ANIMATION_DURATION = 400;
}
0707010000005F000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000003000000000SwayNotificationCenter-0.12.3/src/controlCenter07070100000060000081A4000000000000000000000001690B16EB000049A3000000000000000000000000000000000000004300000000SwayNotificationCenter-0.12.3/src/controlCenter/controlCenter.valanamespace SwayNotificationCenter {
    [GtkTemplate (ui = "/org/erikreider/swaync/ui/control_center.ui")]
    public class ControlCenter : Gtk.ApplicationWindow {
        [GtkChild]
        unowned Gtk.ScrolledWindow window;
        [GtkChild]
        unowned IterBox box;

        private unowned Widgets.Notifications notifications;

        private Gtk.GestureClick blank_window_gesture;
        private bool blank_window_down = false;
        private bool blank_window_in = false;

        private Gtk.EventControllerKey key_controller;

        private SwayncDaemon swaync_daemon;
        private NotiDaemon noti_daemon;

        /** Unsorted list of copies of all notifications */
        private List<Widgets.BaseWidget> widgets;
        private const string[] DEFAULT_WIDGETS = { "title", "dnd", "notifications" };

        private string ?monitor_name = null;

        public ControlCenter (SwayncDaemon swaync_daemon, NotiDaemon noti_daemon) {
            Object (css_name: "blankwindow");
            this.swaync_daemon = swaync_daemon;
            this.noti_daemon = noti_daemon;


            widgets = new List<Widgets.BaseWidget> ();
            widgets.append (new Widgets.Notifications (swaync_daemon, noti_daemon));
            this.notifications = (Widgets.Notifications) widgets.nth_data (0);

            if (swaync_daemon.use_layer_shell) {
                if (!GtkLayerShell.is_supported ()) {
                    stderr.printf ("GTKLAYERSHELL IS NOT SUPPORTED!\n");
                    stderr.printf ("Swaync only works on Wayland!\n");
                    stderr.printf ("If running wayland session, try running:\n");
                    stderr.printf ("\tGDK_BACKEND=wayland swaync\n");
                    Process.exit (1);
                }
                GtkLayerShell.init_for_window (this);
                GtkLayerShell.set_namespace (this, "swaync-control-center");
                set_anchor ();
            }

            this.map.connect (() => {
                set_anchor ();

                unowned Gdk.Surface surface = get_surface ();
                if (!(surface is Gdk.Surface)) {
                    return;
                }

                ulong id = 0;
                id = surface.enter_monitor.connect ((monitor) => {
                    surface.disconnect (id);
                    swaync_daemon.show_blank_windows (monitor);
                });
            });
            this.unmap.connect (swaync_daemon.hide_blank_windows);

            /*
             * Handling of bank window presses (pressing outside of ControlCenter)
             */
            blank_window_gesture = new Gtk.GestureClick ();
            ((Gtk.Widget) this).add_controller (blank_window_gesture);
            blank_window_gesture.touch_only = false;
            blank_window_gesture.exclusive = true;
            blank_window_gesture.button = Gdk.BUTTON_PRIMARY;
            blank_window_gesture.propagation_phase = Gtk.PropagationPhase.BUBBLE;
            blank_window_gesture.pressed.connect ((n_press, x, y) => {
                // Calculate if the clicked coords intersect the ControlCenter
                Graphene.Point click_point = Graphene.Point ()
                     .init ((float) x, (float) y);
                Graphene.Rect ?bounds = null;
                window.compute_bounds (this, out bounds);
                blank_window_in = !(bounds != null && bounds.contains_point (click_point));
                blank_window_down = true;
            });
            blank_window_gesture.released.connect ((n_press, x, y) => {
                // Emit released
                if (!blank_window_down) {
                    return;
                }
                blank_window_down = false;
                if (blank_window_in) {
                    try {
                        swaync_daemon.set_visibility (false);
                    } catch (Error e) {
                        stderr.printf ("ControlCenter BlankWindow Click Error: %s\n",
                                       e.message);
                    }
                }

                if (blank_window_gesture.get_current_sequence () == null) {
                    blank_window_in = false;
                }
            });
            blank_window_gesture.update.connect ((gesture, sequence) => {
                Gtk.GestureSingle gesture_single = (Gtk.GestureSingle) gesture;
                if (sequence != gesture_single.get_current_sequence ()) {
                    return;
                }
                // Calculate if the clicked coords intersect the ControlCenter
                double x, y;
                gesture.get_point (sequence, out x, out y);
                Graphene.Point click_point = Graphene.Point ()
                     .init ((float) x, (float) y);
                Graphene.Rect ?bounds = null;
                window.compute_bounds (this, out bounds);
                if (bounds != null && bounds.contains_point (click_point)) {
                    blank_window_in = false;
                }
            });
            blank_window_gesture.cancel.connect (() => {
                blank_window_down = false;
            });

            // Only use release for closing notifications due to Escape key
            // sometimes being passed through to unfucused application
            // Ex: Firefox in a fullscreen YouTube video
            key_controller = new Gtk.EventControllerKey ();
            key_controller.set_propagation_phase (Gtk.PropagationPhase.CAPTURE);
            ((Gtk.Widget) this).add_controller (key_controller);
            key_controller.key_released.connect (key_released_event_cb);
            key_controller.key_pressed.connect (key_press_event_cb);

            add_widgets ();

            // Change output on config reload
            app.config_reload.connect ((old, config) => {
                string monitor_name = config.control_center_preferred_output;
                if (old == null
                    || old.control_center_preferred_output != monitor_name
                    || this.monitor_name != monitor_name) {
                    this.monitor_name = null;
                    set_anchor ();
                }
            });
        }

        private void key_released_event_cb (uint keyval, uint keycode, Gdk.ModifierType state) {
            if (this.get_focus () is Gtk.Entry) {
                switch (Gdk.keyval_name (keyval)) {
                    case "Escape" :
                        this.set_focus (null);
                        return;
                }
                return;
            }
            switch (Gdk.keyval_name (keyval)) {
                case "Escape" :
                case "Caps_Lock":
                    this.set_visibility (false);
                    return;
            }
        }

        private bool key_press_event_cb (uint keyval, uint keycode, Gdk.ModifierType state) {
            if (get_focus () is Gtk.Text) {
                return false;
            }
            switch (Gdk.keyval_name (keyval)) {
                case "D":
                    try {
                        swaync_daemon.toggle_dnd ();
                    } catch (Error e) {
                        critical ("Error: %s\n", e.message);
                    }
                    break;
                default:
                    return notifications.key_press_event_cb (keyval, keycode, state);
            }
            // Override the builtin list navigation
            return true;
        }

        /** Adds all custom widgets. Removes previous widgets */
        public void add_widgets () {
            // Remove all widgets
            widgets.foreach ((widget) => {
                if (widget.get_parent () == box) {
                    box.remove (widget);
                }
                // Except for notifications. Otherwise we'd loose notifications
                if (widget is Widgets.Notifications) {
                    return;
                }
                widgets.remove (widget);
            });

            string[] w = ConfigModel.instance.widgets.data;
            if (w.length == 0) {
                w = DEFAULT_WIDGETS;
            }

            // Add the notifications widget if not found in the list
            if (!("notifications" in w)) {
                warning ("Notification widget not included in \"widgets\" config. " +
                         "Using default bottom position");
                w += "notifications";
            }
            bool has_notifications = false;
            foreach (string key in w) {
                // Add the widget if it is valid
                bool is_notifications;
                Widgets.BaseWidget ?widget = Widgets.get_widget_from_key (
                    key, swaync_daemon, noti_daemon, out is_notifications);

                if (is_notifications) {
                    if (has_notifications) {
                        warning ("Cannot have multiple \"notifications\" widgets! Skipping\"%s\"",
                                 key);
                        continue;
                    }
                    has_notifications = true;

                    notifications.reload_config ();

                    // Append the notifications widget to the box in the order of the provided list
                    box.append (notifications);
                    continue;
                }
                if (widget == null) {
                    continue;
                }

                // Note: Copies the value into the linked list
                widgets.append (widget);

                unowned Widgets.BaseWidget cloned_widget = widgets.last ().data;
                box.append (cloned_widget);
            }
        }

        /** Resets the UI positions */
        private void set_anchor () {
            PositionX pos_x = ConfigModel.instance.control_center_positionX;
            if (pos_x == PositionX.NONE) {
                pos_x = ConfigModel.instance.positionX;
            }
            PositionY pos_y = ConfigModel.instance.control_center_positionY;
            if (pos_y == PositionY.NONE) {
                pos_y = ConfigModel.instance.positionY;
            }

            if (swaync_daemon.use_layer_shell) {
                // Set the exlusive zone
                int exclusive_zone = ConfigModel.instance.control_center_exclusive_zone ? 0 : 100;
                GtkLayerShell.set_exclusive_zone (this, exclusive_zone);
                // Grabs the keyboard input until closed
                bool keyboard_shortcuts = ConfigModel.instance.keyboard_shortcuts;
                var mode = keyboard_shortcuts ?
                    GtkLayerShell.KeyboardMode.EXCLUSIVE :
                    GtkLayerShell.KeyboardMode.NONE;
                GtkLayerShell.set_keyboard_mode (this, mode);

                // Set layer
                GtkLayerShell.set_layer (
                    this, ConfigModel.instance.control_center_layer.to_layer ());

                // Set whether the control center should cover the whole screen or not
                bool cover_screen = ConfigModel.instance.layer_shell_cover_screen;
                if (cover_screen) {
                    GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.TOP, cover_screen);
                    GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.LEFT, cover_screen);
                    GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.RIGHT, cover_screen);
                    GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.BOTTOM, cover_screen);
                } else {
                    // Fallback to conventional positioning
                    switch (pos_x) {
                        case PositionX.LEFT :
                            GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.LEFT, true);
                            GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.RIGHT, false);
                            break;
                        case PositionX.CENTER:
                            GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.LEFT, true);
                            GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.RIGHT, true);
                            break;
                        default:
                        case PositionX.RIGHT:
                            GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.LEFT, false);
                            GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.RIGHT, true);
                            break;
                    }
                    if (ConfigModel.instance.fit_to_screen) {
                        GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.TOP, true);
                        GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.BOTTOM, true);
                    } else {
                        switch (pos_y) {
                            default:
                            case PositionY.TOP:
                                GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.TOP, true);
                                GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.BOTTOM, false);
                                break;
                            case PositionY.CENTER:
                                GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.TOP, true);
                                GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.BOTTOM, true);
                                break;
                            case PositionY.BOTTOM:
                                GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.TOP, false);
                                GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.BOTTOM, true);
                                break;
                        }
                    }
                }
            }

            // Set the window margins
            window.set_margin_top (ConfigModel.instance.control_center_margin_top);
            window.set_margin_start (ConfigModel.instance.control_center_margin_left);
            window.set_margin_end (ConfigModel.instance.control_center_margin_right);
            window.set_margin_bottom (ConfigModel.instance.control_center_margin_bottom);

            // Anchor window to north/south edges as needed
            Gtk.Align align_x = Gtk.Align.END;
            switch (pos_x) {
                case PositionX.LEFT:
                    align_x = Gtk.Align.START;
                    break;
                case PositionX.CENTER:
                    align_x = Gtk.Align.CENTER;
                    break;
                default:
                case PositionX.RIGHT:
                    align_x = Gtk.Align.END;
                    break;
            }
            Gtk.Align align_y = Gtk.Align.START;
            switch (pos_y) {
                default:
                case PositionY.TOP:
                    align_y = Gtk.Align.START;
                    // Set cc widget position
                    notifications.set_list_is_reversed (false);
                    break;
                case PositionY.CENTER:
                    align_y = Gtk.Align.CENTER;
                    // Set cc widget position
                    notifications.set_list_is_reversed (false);
                    break;
                case PositionY.BOTTOM:
                    align_y = Gtk.Align.END;
                    // Set cc widget position
                    notifications.set_list_is_reversed (true);
                    break;
            }
            // Fit the ControlCenter to the monitor height
            if (ConfigModel.instance.fit_to_screen) {
                align_y = Gtk.Align.FILL;
            }
            // Set the ControlCenter alignment
            window.set_halign (align_x);
            window.set_valign (align_y);

            // Re-set the minimum size
            window.set_propagate_natural_height (
                ConfigModel.instance.control_center_height < 1
                || ConfigModel.instance.fit_to_screen);
            window.set_size_request (ConfigModel.instance.control_center_width,
                                     ConfigModel.instance.control_center_height);
            box.set_size_request (ConfigModel.instance.control_center_width,
                                  ConfigModel.instance.control_center_height);

            // Set the preferred monitor
            string ?monitor_name = ConfigModel.instance.control_center_preferred_output;
            if (this.monitor_name != null) {
                monitor_name = this.monitor_name;
            }
            set_monitor (Functions.try_get_monitor (monitor_name));
        }

        public uint notification_count () {
            return notifications.notification_count ();
        }

        public void close_all_notifications () {
            notifications.close_all_notifications ();
        }

        private void on_visibility_change () {
            // Updates all widgets on visibility change
            foreach (var widget in widgets) {
                widget.on_cc_visibility_change (visible);
            }

            if (this.visible) {
                add_css_class ("open");
            } else {
                remove_css_class ("open");
            }
            swaync_daemon.subscribe_v2 (notification_count (),
                                        noti_daemon.dnd,
                                        this.visible,
                                        swaync_daemon.inhibited);
        }

        public bool toggle_visibility () {
            var cc_visibility = !this.visible;
            set_visibility (cc_visibility);
            return cc_visibility;
        }

        public void set_visibility (bool visibility) {
            if (this.visible == visibility) {
                return;
            }
            if (visibility) {
                // Destroy the wl_surface to get a new "enter-monitor" signal
                ((Gtk.Widget) this).unrealize ();
            }
            this.set_visible (visibility);

            on_visibility_change ();
        }

        public void close_notification (uint32 id, bool dismiss) {
            notifications.close_notification (id, dismiss);
        }

        public void replace_notification (uint32 id, NotifyParams new_params) {
            notifications.replace_notification (id, new_params);
        }

        public void add_notification (NotifyParams param) {
            notifications.add_notification (param);
        }

        public bool get_visibility () {
            return this.visible;
        }

        public void set_monitor (Gdk.Monitor ?monitor) {
            debug ("Setting monitor for ControlCenter: %s", Functions.monitor_to_string (monitor));
            this.monitor_name = monitor == null ? null : monitor.connector;
            GtkLayerShell.set_monitor (this, monitor);
        }
    }
}
07070100000061000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000003800000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets07070100000062000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000004200000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/backlight07070100000063000081A4000000000000000000000001690B16EB00000AB4000000000000000000000000000000000000005100000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/backlight/backlight.valausing GLib;

namespace SwayNotificationCenter.Widgets {
    public class Backlight : BaseWidget {
        public override string widget_name {
            get {
                return "backlight";
            }
        }

        BacklightUtil client;

        Gtk.Label label_widget = new Gtk.Label (null);
        Gtk.Scale slider = new Gtk.Scale.with_range (Gtk.Orientation.HORIZONTAL, 0, 100, 1);

        public Backlight (string suffix, SwayncDaemon swaync_daemon, NotiDaemon noti_daemon) {
            base (suffix, swaync_daemon, noti_daemon);

            Json.Object ?config = get_config (this);
            if (config != null) {
                string ?label = get_prop<string> (config, "label");
                label_widget.set_label (label ?? "Brightness");
                string device = (get_prop<string> (config, "device") ?? "intel_backlight");
                string subsystem = (get_prop<string> (config, "subsystem") ?? "backlight");
                int min = int.max (0, get_prop<int> (config, "min"));

                switch (subsystem) {
                    default :
                    case "backlight":
                        if (subsystem != "backlight") {
                            info ("Invalid subsystem %s for device %s. " +
                                  "Use 'backlight' or 'leds'. Using default: 'backlight'",
                                  subsystem, device);
                        }
                        client = new BacklightUtil ("backlight", device);
                        slider.set_range (min, 100);
                        break;
                    case "leds":
                        client = new BacklightUtil ("leds", device);
                        slider.set_range (min, this.client.get_max_value ());
                        break;
                }
            }

            this.client.brightness_change.connect ((percent) => {
                if (percent < 0) { // invalid device path
                    hide ();
                } else {
                    slider.set_value (percent);
                }
            });

            slider.set_draw_value (false);
            slider.set_round_digits (0);
            slider.set_hexpand (true);
            slider.value_changed.connect (() => {
                this.client.set_brightness.begin ((float) slider.get_value ());
                slider.tooltip_text = ((int) slider.get_value ()).to_string ();
            });

            append (label_widget);
            append (slider);
        }

        public override void on_cc_visibility_change (bool val) {
            if (val) {
                this.client.start ();
            } else {
                this.client.close ();
            }
        }
    }
}
07070100000064000081A4000000000000000000000001690B16EB000011B8000000000000000000000000000000000000005500000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/backlight/backlightUtil.valanamespace SwayNotificationCenter.Widgets {
    class BacklightUtil {
        [DBus (name = "org.freedesktop.login1.Session")]
        interface Login1 : Object {
            public abstract async void set_brightness (string subsystem,
                                                       string name,
                                                       uint32 brightness) throws GLib.Error;
        }

        string path_current;
        string path_max;
        File fd;
        FileMonitor monitor = null;

        int max;

        Login1 login1;
        string device;
        string subsystem;

        public signal void brightness_change (int percent);

        public BacklightUtil (string s, string d) {
            this.subsystem = s;
            this.device = d;

            path_current = Path.build_path (Path.DIR_SEPARATOR_S,
                                            "/sys", "class", subsystem, device, "brightness");
            path_max = Path.build_path (Path.DIR_SEPARATOR_S,
                                        "/sys", "class", subsystem, device, "max_brightness");
            fd = File.new_for_path (path_current);
            if (fd.query_exists ()) {
                set_max_value ();
                try {
                    monitor = fd.monitor (FileMonitorFlags.NONE, null);
                } catch (Error e) {
                    critical ("Error %s\n", e.message);
                }
            } else {
                this.brightness_change (-1);
                warning ("Could not find device %s\n", path_current);
                close ();
            }

            try {
                // setup DBus for setting brightness
                login1 = Bus.get_proxy_sync (BusType.SYSTEM,
                                             "org.freedesktop.login1",
                                             "/org/freedesktop/login1/session/auto");
            } catch (Error e) {
                critical ("Error %s\n", e.message);
            }
        }

        public void start () {
            if (fd.query_exists ()) {
                // get changes made while controlCenter not shown
                get_brightness ();

                connect_monitor ();
            } else {
                this.brightness_change (-1);
                critical ("Could not find device %s\n", path_current);
                close ();
            }
        }

        private void connect_monitor () {
            if (monitor != null) {
                // connect monitor to monitor changes
                monitor.changed.connect ((src, dest, event) => {
                    get_brightness ();
                });
            }
        }

        public void close () {
            if (monitor != null) {
                monitor.cancel ();
            }
        }

        public async void set_brightness (float percent) {
            this.close ();
            if (subsystem == "backlight") {
                int actual = calc_actual (percent);
                login1.set_brightness.begin (subsystem, device, actual);
            } else {
                login1.set_brightness.begin (subsystem, device, (uint32) percent);
            }
            connect_monitor ();
        }

        // get current brightness and emit signal
        private void get_brightness () {
            try {
                var dis = new DataInputStream (fd.read (null));
                string data = dis.read_line (null);
                if (subsystem == "backlight") {
                    int val = calc_percent (int.parse (data));
                    this.brightness_change (val);
                } else {
                    this.brightness_change (int.parse (data));
                }
            } catch (Error e) {
                critical ("Error %s\n", e.message);
            }
        }

        private void set_max_value () {
            try {
                File fd_max = File.new_for_path (path_max);
                DataInputStream dis_max = new DataInputStream (fd_max.read (null));
                string data = dis_max.read_line (null);
                max = int.parse (data);
            } catch (Error e) {
                critical ("Error %s\n", e.message);
            }
        }

        private int calc_percent (int val) {
            return (int) Math.round (val * 100.0 / max);
        }

        private int calc_actual (float val) {
            return (int) Math.round (val * max / 100);
        }

        public int get_max_value () {
            return this.max;
        }
    }
}
07070100000065000081A4000000000000000000000001690B16EB0000166B000000000000000000000000000000000000004800000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/baseWidget.valanamespace SwayNotificationCenter.Widgets {
    public abstract class BaseWidget : Gtk.Box {
        public abstract string widget_name { get; }

        public weak string css_class_name {
            owned get {
                return "widget-%s".printf (widget_name);
            }
        }

        public string key { get; private set; }
        public string suffix { get; private set; }

        public unowned SwayncDaemon swaync_daemon;
        public unowned NotiDaemon noti_daemon;

        public enum ButtonType {
            TOGGLE,
            NORMAL;

            public static ButtonType parse (string value) {
                switch (value) {
                    case "toggle":
                        return ButtonType.TOGGLE;
                    default:
                        return ButtonType.NORMAL;
                }
            }
        }

        protected BaseWidget (string suffix, SwayncDaemon swaync_daemon, NotiDaemon noti_daemon) {
            this.suffix = suffix;
            this.key = widget_name + (suffix.length > 0 ? "#%s".printf (suffix) : "");
            this.swaync_daemon = swaync_daemon;
            this.noti_daemon = noti_daemon;

            set_overflow (Gtk.Overflow.HIDDEN);
            add_css_class ("widget");
            add_css_class (css_class_name);
            if (suffix.length > 0) {
                add_css_class (suffix);
            }
        }

        protected Json.Object ?get_config (Gtk.Widget widget) {
            unowned OrderedHashTable<Json.Object> config
                = ConfigModel.instance.widget_config;
            string ?orig_key = null;
            Json.Object ?props = null;
            bool result = config.lookup_extended (key, out orig_key, out props);
            if (!result || orig_key == null || props == null) {
                warning ("%s: Config not found! Using default config...\n", key);
                return null;
            }
            return props;
        }

        public virtual void on_cc_visibility_change (bool value) {
        }

        protected T ?get_prop<T> (Json.Object config, string value_key, out bool found = null) {
            found = false;
            if (!config.has_member (value_key)) {
                debug ("%s: Config doesn't have key: %s!\n", key, value_key);
                return null;
            }
            var member = config.get_member (value_key);

            Type base_type = Functions.get_base_type (member.get_value_type ());

            Type generic_base_type = Functions.get_base_type (typeof (T));
            // Convert all INTs to INT64
            if (generic_base_type == Type.INT) {
                generic_base_type = Type.INT64;
            }

            if (!base_type.is_a (generic_base_type)) {
                warning ("%s: Config type %s doesn't match: %s!\n",
                         key,
                         typeof (T).name (),
                         member.get_value_type ().name ());
                return null;
            }
            found = true;
            switch (generic_base_type) {
                case Type.STRING :
                    return member.get_string ();
                case Type.INT64 :
                    return (int) member.get_int ();
                case Type.BOOLEAN :
                    return member.get_boolean ();
                default:
                    found = false;
                    return null;
            }
        }

        protected Json.Array ?get_prop_array (Json.Object config, string value_key) {
            if (!config.has_member (value_key)) {
                debug ("%s: Config doesn't have key: %s!\n", key, value_key);
                return null;
            }
            var member = config.get_member (value_key);
            if (member.get_node_type () != Json.NodeType.ARRAY) {
                debug ("Unable to find Json Array for member %s", value_key);
            }
            return config.get_array_member (value_key);
        }

        protected Action[] parse_actions (Json.Array actions) {
            Action[] res = new Action[actions.get_length ()];
            for (int i = 0; i < actions.get_length (); i++) {
                string label =
                    actions.get_object_element (i).get_string_member_with_default ("label",
                                                                                   "label");
                string command =
                    actions.get_object_element (i).get_string_member_with_default ("command", "");
                string t = actions.get_object_element (i).get_string_member_with_default ("type",
                                                                                          "normal");
                ButtonType type = ButtonType.parse (t);
                string update_command =
                    actions.get_object_element (i).get_string_member_with_default ("update-command",
                                                                                   "");
                bool active =
                    actions.get_object_element (i).get_boolean_member_with_default ("active",
                                                                                    false);
                res[i] = Action () {
                    label = label,
                    command = command,
                    type = type,
                    update_command = update_command,
                    active = active
                };
            }
            return res;
        }

        protected async void execute_command (string cmd, string[] env_additions = {}) {
            string msg = "";
            yield Functions.execute_command (cmd, env_additions, out msg);
        }
    }
}
07070100000066000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000004400000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/buttonsGrid07070100000067000081A4000000000000000000000001690B16EB00000919000000000000000000000000000000000000005500000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/buttonsGrid/buttonsGrid.valausing GLib;

namespace SwayNotificationCenter.Widgets {
    public class ButtonsGrid : BaseWidget {
        public override string widget_name {
            get {
                return "buttons-grid";
            }
        }

        Action[] actions;
        // 7 is the default Gtk.FlowBox.max_children_per_line
        int buttons_per_row = 7;
        List<ToggleButton> toggle_buttons;

        public ButtonsGrid (string suffix, SwayncDaemon swaync_daemon, NotiDaemon noti_daemon) {
            base (suffix, swaync_daemon, noti_daemon);

            Json.Object ?config = get_config (this);
            if (config != null) {
                Json.Array a = get_prop_array (config, "actions");
                if (a != null) {
                    actions = parse_actions (a);
                }

                bool bpr_found = false;
                int bpr = get_prop<int> (config, "buttons-per-row", out bpr_found);
                if (bpr_found) {
                    buttons_per_row = bpr;
                }
            }

            Gtk.FlowBox container = new Gtk.FlowBox ();
            container.set_max_children_per_line (buttons_per_row);
            container.set_selection_mode (Gtk.SelectionMode.NONE);
            container.set_hexpand (true);
            append (container);

            // add action to container
            foreach (var act in actions) {
                switch (act.type) {
                    case ButtonType.TOGGLE :
                        ToggleButton tb = new ToggleButton (act.label, act.command,
                                                            act.update_command, act.active);
                        container.insert (tb, -1);
                        toggle_buttons.append (tb);
                        break;
                    default:
                        Gtk.Button b = new Gtk.Button.with_label (act.label);
                        b.clicked.connect (() => execute_command.begin (act.command));
                        container.insert (b, -1);
                        break;
                }
            }
        }

        public override void on_cc_visibility_change (bool value) {
            if (value) {
                foreach (var tb in toggle_buttons) {
                    tb.on_update.begin ();
                }
            }
        }
    }
}
07070100000068000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000003C00000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/dnd07070100000069000081A4000000000000000000000001690B16EB0000073D000000000000000000000000000000000000004500000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/dnd/dnd.valanamespace SwayNotificationCenter.Widgets {
    public class Dnd : BaseWidget {
        public override string widget_name {
            get {
                return "dnd";
            }
        }

        Gtk.Label title_widget;
        Gtk.Switch dnd_button;

        // Default config values
        string title = "Do Not Disturb";

        public Dnd (string suffix, SwayncDaemon swaync_daemon, NotiDaemon noti_daemon) {
            base (suffix, swaync_daemon, noti_daemon);

            Json.Object ?config = get_config (this);
            if (config != null) {
                // Get title
                string ?title = get_prop<string> (config, "text");
                if (title != null) {
                    this.title = title;
                }
            }

            // Title
            title_widget = new Gtk.Label (title);
            title_widget.set_hexpand (true);
            title_widget.set_halign (Gtk.Align.START);
            append (title_widget);

            // Dnd button
            dnd_button = new Gtk.Switch () {
                active = noti_daemon.dnd,
            };
            dnd_button.notify["active"].connect (switch_active_changed_cb);
            noti_daemon.on_dnd_toggle.connect ((dnd) => {
                dnd_button.notify["active"].disconnect (switch_active_changed_cb);
                dnd_button.set_active (dnd);
                dnd_button.notify["active"].connect (switch_active_changed_cb);
            });

            dnd_button.set_can_focus (false);
            dnd_button.valign = Gtk.Align.CENTER;
            // Backwards compatible towards older CSS stylesheets
            dnd_button.add_css_class ("control-center-dnd");
            append (dnd_button);
        }

        private void switch_active_changed_cb () {
            noti_daemon.dnd = dnd_button.active;
        }
    }
}
0707010000006A000081A4000000000000000000000001690B16EB00000938000000000000000000000000000000000000004500000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/factory.valanamespace SwayNotificationCenter.Widgets {
    public static BaseWidget ?get_widget_from_key (owned string key,
                                                   SwayncDaemon swaync_daemon,
                                                   NotiDaemon noti_daemon,
                                                   out bool is_notifications) {
        is_notifications = false;

        string[] key_seperated = key.split ("#");
        string suffix = "";
        if (key_seperated.length > 0) {
            key = key_seperated[0];
        }
        if (key_seperated.length > 1) {
            suffix = key_seperated[1];
        }
        BaseWidget widget;
        switch (key) {
            case "notifications" :
                is_notifications = true;
                message ("Loading widget: widget-notifications");
                return null;
            case "title":
                widget = new Title (suffix, swaync_daemon, noti_daemon);
                break;
            case "dnd":
                widget = new Dnd (suffix, swaync_daemon, noti_daemon);
                break;
            case "label":
                widget = new Label (suffix, swaync_daemon, noti_daemon);
                break;
            case "mpris":
                widget = new Mpris.Mpris (suffix, swaync_daemon, noti_daemon);
                break;
            case "menubar":
                widget = new Menubar (suffix, swaync_daemon, noti_daemon);
                break;
            case "buttons-grid":
                widget = new ButtonsGrid (suffix, swaync_daemon, noti_daemon);
                break;
            case "slider":
                widget = new Slider (suffix, swaync_daemon, noti_daemon);
                break;
#if HAVE_PULSE_AUDIO
            case "volume":
                widget = new Volume (suffix, swaync_daemon, noti_daemon);
                break;
#endif
            case "backlight":
                widget = new Backlight (suffix, swaync_daemon, noti_daemon);
                break;
            case "inhibitors":
                widget = new Inhibitors (suffix, swaync_daemon, noti_daemon);
                break;
            default:
                warning ("Could not find widget: \"%s\"!", key);
                return null;
        }
        message ("Loading widget: %s", widget.widget_name);
        return widget;
    }
}
0707010000006B000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000004300000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/inhibitors0707010000006C000081A4000000000000000000000001690B16EB00000A47000000000000000000000000000000000000005300000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/inhibitors/inhibitors.valanamespace SwayNotificationCenter.Widgets {
    public class Inhibitors : BaseWidget {
        public override string widget_name {
            get {
                return "inhibitors";
            }
        }

        Gtk.Label title_widget;
        Gtk.Button clear_all_button;

        // Default config values
        string title = "Inhibitors";
        bool has_clear_all_button = true;
        string button_text = "Clear All";

        public Inhibitors (string suffix, SwayncDaemon swaync_daemon, NotiDaemon noti_daemon) {
            base (suffix, swaync_daemon, noti_daemon);

            swaync_daemon.inhibited_changed.connect ((length) => {
                if (!swaync_daemon.inhibited) {
                    hide ();
                    return;
                }
                show ();
                title_widget.set_text ("%s %u".printf (title, length));
            });

            Json.Object ?config = get_config (this);
            if (config != null) {
                // Get title
                string ?title = get_prop<string> (config, "text");
                if (title != null) {
                    this.title = title;
                }
                // Get has clear-all-button
                bool found_clear_all;
                bool ?has_clear_all_button = get_prop<bool> (
                    config, "clear-all-button", out found_clear_all);
                if (found_clear_all) {
                    this.has_clear_all_button = has_clear_all_button;
                }
                // Get button text
                string ?button_text = get_prop<string> (config, "button-text");
                if (button_text != null) {
                    this.button_text = button_text;
                }
            }

            title_widget = new Gtk.Label (title);
            title_widget.set_halign (Gtk.Align.START);
            title_widget.set_hexpand (true);
            title_widget.show ();
            append (title_widget);

            if (has_clear_all_button) {
                clear_all_button = new Gtk.Button.with_label (button_text);
                clear_all_button.clicked.connect (() => {
                    try {
                        swaync_daemon.clear_inhibitors ();
                    } catch (Error e) {
                        critical ("Error: %s\n", e.message);
                    }
                });
                clear_all_button.set_can_focus (false);
                clear_all_button.valign = Gtk.Align.CENTER;
                clear_all_button.show ();
                append (clear_all_button);
            }

            hide ();
        }
    }
}
0707010000006D000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000003E00000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/label0707010000006E000081A4000000000000000000000001690B16EB000006EE000000000000000000000000000000000000004900000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/label/label.valanamespace SwayNotificationCenter.Widgets {
    public class Label : BaseWidget {
        public override string widget_name {
            get {
                return "label";
            }
        }

        Gtk.Label label_widget;

        // Default config values
        string text = "Label Text";
        int max_lines = 5;

        public Label (string suffix, SwayncDaemon swaync_daemon, NotiDaemon noti_daemon) {
            base (suffix, swaync_daemon, noti_daemon);

            Json.Object ?config = get_config (this);
            if (config != null) {
                // Get text
                string ?text = get_prop<string> (config, "text");
                if (text != null) {
                    this.text = text;
                }
                // Get max lines
                int ?max_lines = get_prop<int> (config, "max-lines");
                if (max_lines != null) {
                    this.max_lines = max_lines;
                }
            }

            label_widget = new Gtk.Label (null);
            label_widget.set_text (text);

            label_widget.set_ellipsize (Pango.EllipsizeMode.END);
            label_widget.set_wrap (true);
            label_widget.set_lines (max_lines);
            // Without this and pack_start fill, the label would expand to
            // the monitors full width... GTK bug!...
            label_widget.set_max_width_chars (0);
            label_widget.set_wrap_mode (Pango.WrapMode.WORD_CHAR);
            label_widget.set_justify (Gtk.Justification.LEFT);
            label_widget.set_xalign (0.0f);
            label_widget.set_yalign (0.0f);
            label_widget.set_halign (Gtk.Align.FILL);
            label_widget.set_hexpand (true);

            append (label_widget);
        }
    }
}
0707010000006F000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000004000000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/menubar07070100000070000081A4000000000000000000000001690B16EB00002926000000000000000000000000000000000000004D00000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/menubar/menubar.valausing GLib;

namespace SwayNotificationCenter.Widgets {
    public enum MenuType {
        BUTTONS,
        MENU
    }

    public enum Position {
        LEFT,
        RIGHT
    }

    public struct ConfigObject {
        string ?name;
        MenuType ?type;
        string ?label;
        Position ?position;
        Action[] actions;
        Gtk.Revealer ?revealer;
        int animation_duration;
        Gtk.RevealerTransitionType animation_type;
    }

    public struct Action {
        string ?label;
        string ?command;
        BaseWidget.ButtonType ?type;
        string ?update_command;
        bool ?active;
    }

    public class Menubar : BaseWidget {
        public override string widget_name {
            get {
                return "menubar";
            }
        }

        Gtk.Box left_container;
        Gtk.Box right_container;

        List<ConfigObject ?> menu_objects;
        List<ToggleButton> toggle_buttons;

        public Menubar (string suffix, SwayncDaemon swaync_daemon, NotiDaemon noti_daemon) {
            base (suffix, swaync_daemon, noti_daemon);
            set_orientation (Gtk.Orientation.VERTICAL);
            set_hexpand (true);

            Json.Object ?config = get_config (this);
            if (config != null) {
                parse_config_objects (config);
            }

            Gtk.Box topbar_container = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
            topbar_container.add_css_class ("menu-button-bar");
            append (topbar_container);

            left_container = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0) {
                overflow = Gtk.Overflow.HIDDEN,
                hexpand = true,
                halign = Gtk.Align.START,
            };
            left_container.add_css_class ("widget-menubar-container");
            left_container.add_css_class ("start");
            topbar_container.append (left_container);
            right_container = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0) {
                overflow = Gtk.Overflow.HIDDEN,
                hexpand = true,
                halign = Gtk.Align.END,
            };
            right_container.add_css_class ("widget-menubar-container");
            right_container.add_css_class ("end");
            topbar_container.append (right_container);

            for (int i = 0; i < menu_objects.length (); i++) {
                unowned ConfigObject ?obj = menu_objects.nth_data (i);
                add_menu (ref obj);
            }

            foreach (var obj in menu_objects) {
                obj.revealer ?.set_reveal_child (false);
            }
        }

        void add_menu (ref unowned ConfigObject ?obj) {
            switch (obj.type) {
                case MenuType.BUTTONS :
                    Gtk.Box container = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
                    if (obj.name != null) {
                        container.add_css_class (obj.name);
                    }

                    foreach (Action a in obj.actions) {
                        switch (a.type) {
                            case ButtonType.TOGGLE :
                                ToggleButton tb = new ToggleButton (a.label, a.command,
                                                                    a.update_command, a.active);
                                container.append (tb);
                                toggle_buttons.append (tb);
                                break;
                                default :
                                Gtk.Button b = new Gtk.Button.with_label (a.label);
                                b.clicked.connect (() => execute_command.begin (a.command));
                                container.append (b);
                                break;
                        }
                    }
                    switch (obj.position) {
                        case Position.LEFT :
                            left_container.append (container);
                            break;
                        case Position.RIGHT :
                            right_container.append (container);
                            break;
                    }
                    break;
                case MenuType.MENU :
                    Gtk.ToggleButton show_button = new Gtk.ToggleButton.with_label (obj.label);

                    Gtk.Box menu = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
                    if (obj.name != null) {
                        menu.add_css_class (obj.name);
                    }

                    Gtk.Revealer r = new Gtk.Revealer ();
                    r.set_child (menu);
                    r.set_transition_duration (obj.animation_duration);
                    r.set_transition_type (obj.animation_type);
                    obj.revealer = r;

                    // Make sure that the toggle buttons state is always synced
                    // with the revealers visibility.
                    r.bind_property ("child-revealed",
                                     show_button, "active", BindingFlags.SYNC_CREATE, null, null);

                    show_button.clicked.connect (() => {
                    bool visible = !r.get_reveal_child ();
                    foreach (var o in menu_objects) {
                        o.revealer ?.set_reveal_child (false);
                    }
                    r.set_reveal_child (visible);
                });

                    foreach (var a in obj.actions) {
                        switch (a.type) {
                            case ButtonType.TOGGLE :
                                ToggleButton tb = new ToggleButton (a.label, a.command,
                                                                    a.update_command, a.active);
                                tb.set_hexpand (true);
                                menu.append (tb);
                                toggle_buttons.append (tb);
                                break;
                                default :
                                Gtk.Button b = new Gtk.Button.with_label (a.label);
                                b.set_hexpand (true);
                                b.clicked.connect (() => execute_command.begin (a.command));
                                menu.append (b);
                                break;
                        }
                    }

                    switch (obj.position) {
                        case Position.RIGHT:
                            right_container.append (show_button);
                            break;
                        case Position.LEFT:
                            left_container.append (show_button);
                            break;
                    }

                    append (r);
                    break;
            }
        }

        protected void parse_config_objects (Json.Object config) {
            var elements = config.get_members ();

            menu_objects = new List<ConfigObject ?> ();
            for (int i = 0; i < elements.length (); i++) {
                string e = elements.nth_data (i);
                Json.Object ?obj = config.get_object_member (e);

                if (obj == null) {
                    continue;
                }

                string[] key = e.split ("#");
                string t = key[0];
                MenuType type = MenuType.BUTTONS;
                if (t == "buttons") {
                    type = MenuType.BUTTONS;
                } else if (t == "menu") {
                    type = MenuType.MENU;
                } else {
                    info ("Invalid type for menu-object - valid options: " +
                          "'menu' || 'buttons' using default");
                }

                string name = key[1];

                string ?p = get_prop<string> (obj, "position");
                Position pos;
                if (p != "left" && p != "right") {
                    pos = Position.RIGHT;
                    info ("No position for menu-object given using default");
                } else if (p == "right") {
                    pos = Position.RIGHT;
                } else {
                    pos = Position.LEFT;
                }

                Json.Array ?actions = get_prop_array (obj, "actions");
                if (actions == null) {
                    info ("Error parsing actions for menu-object");
                }

                string ?label = get_prop<string> (obj, "label");
                if (label == null) {
                    label = "Menu";
                    info ("No label for menu-object given using default");
                }

                int duration = int.max (0, get_prop<int> (obj, "animation-duration"));
                if (duration == 0) {
                    duration = 250;
                }

                string ?animation_type = get_prop<string> (obj, "animation-type");
                if (animation_type == null) {
                    animation_type = "slide_down";
                    info ("No animation-type for menu-object given using default");
                }

                Gtk.RevealerTransitionType revealer_type;

                switch (animation_type) {
                    default :
                    case "none" :
                        revealer_type = Gtk.RevealerTransitionType.NONE;
                        break;
                    case "slide_up":
                        revealer_type = Gtk.RevealerTransitionType.SLIDE_UP;
                        break;
                    case "slide_down":
                        revealer_type = Gtk.RevealerTransitionType.SLIDE_DOWN;
                        break;
                }

                Action[] actions_list = parse_actions (actions);
                menu_objects.append (ConfigObject () {
                    name = name,
                    type = type,
                    label = label,
                    position = pos,
                    actions = actions_list,
                    revealer = null,
                    animation_duration = duration,
                    animation_type = revealer_type,
                });
            }
        }

        public override void on_cc_visibility_change (bool val) {
            if (!val) {
                foreach (var obj in menu_objects) {
                    obj.revealer ?.set_reveal_child (false);
                }
            } else {
                foreach (var tb in toggle_buttons) {
                    tb.on_update.begin ();
                }
            }
        }
    }
}
07070100000071000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000003E00000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/mpris07070100000072000081A4000000000000000000000001690B16EB00000FE1000000000000000000000000000000000000004E00000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/mpris/interfaces.valanamespace SwayNotificationCenter.Widgets.Mpris {
    public class MprisSource : Object {
        public MprisMediaPlayer media_player { private set; get; }
        private DbusPropChange props;

        public signal void properties_changed (string iface,
                                               HashTable<string, Variant> changed,
                                               string[] invalid);

        public const string INTERFACE_PATH = "/org/mpris/MediaPlayer2";

        private MprisSource (MprisMediaPlayer _media_player, DbusPropChange _props) {
            this.media_player = _media_player;
            this.props = _props;
            this.props.properties_changed.connect (
                (i, c, inv) => properties_changed (i, c, inv));
        }

        public static MprisSource ?get_player (string bus_name) {
            MprisMediaPlayer ?player;
            DbusPropChange ?props;
            try {
                player = Bus.get_proxy_sync (BusType.SESSION, bus_name, INTERFACE_PATH);
            } catch (Error e) {
                message (e.message);
                return null;
            }
            try {
                props = Bus.get_proxy_sync (BusType.SESSION, bus_name, INTERFACE_PATH);
            } catch (Error e) {
                message (e.message);
                return null;
            }
            if (player == null || props == null) {
                return null;
            }
            return new MprisSource (player, props);
        }

        public Variant ?get_mpris_player_prop (string property_name) {
            try {
                return props.get ("org.mpris.MediaPlayer2.Player", property_name);
            } catch (Error e) {}
            return null;
        }

        public Variant ?get_mpris_prop (string property_name) {
            try {
                return props.get ("org.mpris.MediaPlayer2", property_name);
            } catch (Error e) {}
            return null;
        }
    }

    /** MPRIS uses properties_changed for player changes */
    [DBus (name = "org.freedesktop.DBus.Properties")]
    public interface DbusPropChange : Object {
        public signal void properties_changed (string iface,
                                               HashTable<string, Variant> changed,
                                               string[] invalid);

        public abstract Variant get (string iface_name, string property_name) throws Error;
    }

    [DBus (name = "org.mpris.MediaPlayer2")]
    public interface MprisProps : Object {
        public abstract string desktop_entry { owned get; }
        public abstract string identity { owned get; }
    }

    [DBus (name = "org.mpris.MediaPlayer2.Player")]
    public interface MprisMediaPlayer : MprisProps {
        public abstract async void next () throws Error;
        public abstract async void previous () throws Error;
        public abstract async void pause () throws Error;
        public abstract async void play_pause () throws Error;
        public abstract async void stop () throws Error;
        public abstract async void play () throws Error;

        public abstract string playback_status { owned get; }
        public abstract HashTable<string, Variant> metadata { owned get; }
        public abstract bool can_go_next { owned get; }
        public abstract bool can_go_previous { owned get; }
        public abstract bool can_play { owned get; }
        public abstract bool can_pause { owned get; }
        public abstract bool can_control { owned get; }
        public abstract bool can_seek { owned get; }

        public abstract bool shuffle { owned get; set; }
        public abstract string loop_status { owned get; set; }
    }

    [DBus (name = "org.freedesktop.DBus")]
    public interface DBusInterface : Object {
        public abstract string[] list_names () throws Error;
        public signal void name_owner_changed (string name,
                                               string old_owner,
                                               string new_owner);
    }
}
07070100000073000081A4000000000000000000000001690B16EB00002EF8000000000000000000000000000000000000004900000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/mpris/mpris.valanamespace SwayNotificationCenter.Widgets.Mpris {
    public enum AlbumArtState {
        ALWAYS, WHEN_AVAILABLE, NEVER;

        public static AlbumArtState parse (string value) {
            switch (value) {
                default:
                case "always":
                    return AlbumArtState.ALWAYS;
                case "when-available":
                    return AlbumArtState.WHEN_AVAILABLE;
                case "never":
                    return AlbumArtState.NEVER;
            }
        }
    }

    public struct Config {
        [Version (deprecated = true, replacement = "CSS root variable")]
        int image_size;
        AlbumArtState show_album_art;
        bool autohide;
        string[] blacklist;
        bool loop_carousel;
    }

    public class Mpris : BaseWidget {
        public override string widget_name {
            get {
                return "mpris";
            }
        }

        const string MPRIS_PREFIX = "org.mpris.MediaPlayer2.";
        HashTable<string, MprisPlayer> players = new HashTable<string, MprisPlayer> (str_hash,
                                                                                     str_equal);

        DBusInterface dbus_iface;

        Gtk.Button button_prev;
        Gtk.Button button_next;
        Gtk.Box carousel_box;
        Adw.Carousel carousel;
        Adw.CarouselIndicatorDots carousel_dots;

        // Default config values
        Config mpris_config = Config () {
            image_size = -1,
            show_album_art = AlbumArtState.ALWAYS,
            autohide = false,
            loop_carousel = false,
        };

        public Mpris (string suffix, SwayncDaemon swaync_daemon, NotiDaemon noti_daemon) {
            base (suffix, swaync_daemon, noti_daemon);
            set_orientation (Gtk.Orientation.VERTICAL);
            set_valign (Gtk.Align.START);
            set_vexpand (false);

            carousel_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0) {
                visible = true,
            };

            button_prev = new Gtk.Button.from_icon_name ("go-previous") {
                has_frame = false,
                visible = false,
            };
            button_prev.clicked.connect (() => change_carousel_position (-1));

            button_next = new Gtk.Button.from_icon_name ("go-next") {
                has_frame = false,
                visible = false,
            };
            button_next.clicked.connect (() => change_carousel_position (1));

            carousel = new Adw.Carousel () {
                visible = true,
            };
            carousel.allow_scroll_wheel = true;
            carousel.page_changed.connect ((index) => {
                if (carousel.n_pages <= 1) {
                    button_prev.sensitive = false;
                    button_next.sensitive = false;
                    return;
                }
                button_prev.sensitive = (index > 0) || mpris_config.loop_carousel;
                button_next.sensitive = (index < carousel.n_pages - 1) ||
                    mpris_config.loop_carousel;
            });

            carousel_box.append (button_prev);
            carousel_box.append (carousel);
            carousel_box.append (button_next);
            append (carousel_box);

            carousel_dots = new Adw.CarouselIndicatorDots ();
            carousel_dots.set_carousel (carousel);
            carousel_dots.set_halign (Gtk.Align.CENTER);
            carousel_dots.set_valign (Gtk.Align.CENTER);
            carousel_dots.set_visible (false);
            append (carousel_dots);

            // Config
            Json.Object ?config = get_config (this);
            if (config != null) {
                // Get image-size
                bool image_size_found;
                int ?image_size = get_prop<int> (config, "image-size", out image_size_found);
                if (image_size_found && image_size != null) {
                    mpris_config.image_size = image_size;
                }

                bool show_art_found;
                string ?show_album_art = get_prop<string> (config, "show-album-art",
                                                           out show_art_found);
                if (show_art_found && show_album_art != null) {
                    mpris_config.show_album_art = AlbumArtState.parse (show_album_art);
                }

                Json.Array ?blacklist = get_prop_array (config, "blacklist");
                if (blacklist != null) {
                    mpris_config.blacklist = new string[blacklist.get_length ()];
                    for (int i = 0; i < blacklist.get_length (); i++) {
                        if (blacklist.get_element (i).get_node_type () != Json.NodeType.VALUE) {
                            warning ("Blacklist entries should be strings");
                            continue;
                        }
                        mpris_config.blacklist[i] = blacklist.get_string_element (i);
                    }
                }

                // Get autohide
                bool autohide_found;
                bool ?autohide = get_prop<bool> (config, "autohide", out autohide_found);
                if (autohide_found) {
                    mpris_config.autohide = autohide;
                }

                // Get loop
                bool loop_carousel_found;
                bool ?loop_carousel = get_prop<bool> (config, "loop-carousel",
                                                      out loop_carousel_found);
                if (loop_carousel_found) {
                    mpris_config.loop_carousel = loop_carousel;
                }
            }

            hide ();
            try {
                setup_mpris ();
            } catch (Error e) {
                critical ("MPRIS Widget error: %s", e.message);
            }
        }

        private void setup_mpris () throws Error {
            dbus_iface = Bus.get_proxy_sync (BusType.SESSION,
                                             "org.freedesktop.DBus",
                                             "/org/freedesktop/DBus");
            string[] names = dbus_iface.list_names ();
            foreach (string name in names) {
                if (!name.has_prefix (MPRIS_PREFIX)) {
                    continue;
                }
                if (is_blacklisted (name)) {
                    continue;
                }
                if (check_player_exists (name)) {
                    return;
                }
                MprisSource ?source = MprisSource.get_player (name);
                if (source != null) {
                    add_player (name, source);
                }
            }

            dbus_iface.name_owner_changed.connect ((name, old_owner, new_owner) => {
                if (!name.has_prefix (MPRIS_PREFIX)) {
                    return;
                }
                if (old_owner != "") {
                    remove_player (name);
                    return;
                }
                if (is_blacklisted (name)) {
                    return;
                }
                if (check_player_exists (name)) {
                    return;
                }
                MprisSource ?source = MprisSource.get_player (name);
                if (source != null) {
                    add_player (name, source);
                }
            });
        }

        private bool check_player_exists (string name) {
            foreach (string name_check in players.get_keys_as_array ()) {
                if (name_check.has_prefix (name)
                    || name.has_prefix (name_check)) {
                    return true;
                }
            }
            return false;
        }

        private bool check_player_metadata_empty (string name) {
            MprisPlayer ?player = players.lookup (name);
            if (player == null) {
                return true;
            }
            HashTable<string, Variant> metadata = player.source.media_player.metadata;
            if (metadata == null || metadata.size () == 0) {
                debug ("Metadata is empty");
                return true;
            }
            return false;
        }

        private bool check_carousel_has_player (MprisPlayer player) {
            return player != null && player.parent == carousel;
        }

        private void add_player_to_carousel (string name) {
            MprisPlayer ?player = players.lookup (name);
            if (player == null || check_carousel_has_player (player)) {
                return;
            }
            // HACK: The carousel doesn't focus the prepended player when not mapped.
            carousel.append (player);
            carousel.reorder (player, 0);

            if (carousel.n_pages > 1) {
                button_prev.show ();
                button_next.show ();
                carousel_dots.set_visible (true);
                // Scroll to the new player
                carousel.scroll_to (player, false);
            }

            if (!visible) {
                show ();
            }
        }

        private void add_player (string name, MprisSource source) {
            MprisPlayer player = new MprisPlayer (source, mpris_config);
            player.add_css_class ("%s-player".printf (css_class_name));
            players.set (name, player);

            if (mpris_config.autohide) {
                player.content_updated.connect (() => {
                    if (!check_player_metadata_empty (name)) {
                        add_player_to_carousel (name);
                    } else {
                        remove_player_from_carousel (name);
                    }
                });
                if (check_player_metadata_empty (name)) {
                    return;
                }
            }

            add_player_to_carousel (name);
        }

        private void remove_player_from_carousel (string name) {
            MprisPlayer ?player = players.lookup (name);
            if (player == null || !check_carousel_has_player (player)) {
                return;
            }
            carousel.remove (player);

            uint children_length = carousel.n_pages;
            if (children_length == 0) {
                hide ();
            }
            if (children_length <= 1) {
                button_prev.hide ();
                button_next.hide ();
                carousel_dots.set_visible (false);
            }
        }

        private void remove_player (string name) {
            string ?key;
            MprisPlayer ?player;
            bool result = players.lookup_extended (name, out key, out player);
            if (!result || key == null || player == null) {
                return;
            }

            remove_player_from_carousel (name);

            player.before_destroy ();
            players.remove (name);
        }

        private void change_carousel_position (int delta) {
            uint children_length = carousel.n_pages;
            if (children_length == 0) {
                return;
            }
            uint position;
            if (mpris_config.loop_carousel) {
                position = ((uint) carousel.position + delta) % children_length;
            } else {
                position = ((uint) carousel.position + delta)
                     .clamp (0, (children_length - 1));
            }
            carousel.scroll_to (carousel.get_nth_page (position), true);
        }

        private bool is_blacklisted (string name) {
            foreach (string blacklistedPattern in mpris_config.blacklist) {
                if (blacklistedPattern == null || blacklistedPattern.length == 0) {
                    continue;
                }
                if (GLib.Regex.match_simple (blacklistedPattern, name, RegexCompileFlags.DEFAULT,
                                             0)) {
                    message ("\"%s\" is blacklisted", name);
                    return true;
                }
            }
            return false;
        }
    }
}
07070100000074000081A4000000000000000000000001690B16EB000049C8000000000000000000000000000000000000005000000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/mpris/mpris_player.valanamespace SwayNotificationCenter.Widgets.Mpris {
    [GtkTemplate (ui = "/org/erikreider/swaync/ui/mpris_player.ui")]
    public class MprisPlayer : Underlay {
        [GtkChild]
        public unowned Gtk.Label title;
        [GtkChild]
        unowned Gtk.Label sub_title;

        [GtkChild]
        unowned Gtk.Image album_art;

        [GtkChild]
        unowned Gtk.Picture background_picture;

        [GtkChild]
        unowned Gtk.Button button_shuffle;
        [GtkChild]
        unowned Gtk.Button button_prev;
        [GtkChild]
        unowned Gtk.Button button_play_pause;
        [GtkChild]
        unowned Gtk.Button button_next;
        [GtkChild]
        unowned Gtk.Button button_repeat;

        public MprisSource source { construct; get; }

        private const double UNSELECTED_OPACITY = 0.5;

        public const string ICON_REPEAT = "media-playlist-repeat-symbolic";
        public const string ICON_REPEAT_SONG = "media-playlist-repeat-song-symbolic";

        public const string ICON_PLAY = "media-playback-start-symbolic";
        public const string ICON_PAUSE = "media-playback-pause-symbolic";

        private Cancellable album_art_cancellable = new Cancellable ();
        private string prev_art_data;
        private DesktopAppInfo ?desktop_entry = null;

        private unowned Config mpris_config;

        public signal void content_updated ();

        public MprisPlayer (MprisSource source, Config mpris_config) {
            Object (source : source);
            this.mpris_config = mpris_config;

            source.properties_changed.connect (properties_changed);

            // Init content
            update_content ();

            /* Callbacks */

            // Shuffle
            button_shuffle.clicked.connect (() => {
                source.media_player.shuffle = !source.media_player.shuffle;
                // Wait until dbus value has updated
                button_shuffle.sensitive = false;
            });
            // Repeat
            button_repeat.clicked.connect (() => {
                switch (source.media_player.loop_status) {
                    case "None":
                        source.media_player.loop_status = "Playlist";
                        break;
                    case "Playlist":
                        source.media_player.loop_status = "Track";
                        break;
                    case "Track":
                        source.media_player.loop_status = "None";
                        break;
                    default:
                        return;
                }
                // Wait until dbus value has updated
                button_repeat.sensitive = false;
            });
            // Prev
            button_prev.clicked.connect (() => {
                source.media_player.previous.begin (() => {
                    update_buttons (source.media_player.metadata);
                });
            });
            // Next
            button_next.clicked.connect (() => {
                source.media_player.next.begin (() => {
                    update_buttons (source.media_player.metadata);
                });
            });
            // Play/Pause
            button_play_pause.clicked.connect (() => {
                source.media_player.play_pause.begin (() => {
                    update_buttons (source.media_player.metadata);
                });
            });
            album_art.set_pixel_size (mpris_config.image_size);
            album_art.set_visible (mpris_config.show_album_art == AlbumArtState.ALWAYS);
        }

        public void before_destroy () {
            source.properties_changed.disconnect (properties_changed);
        }

        private void properties_changed (string iface,
                                         HashTable<string, Variant> changed,
                                         string[] invalid) {
            var metadata = source.media_player.metadata;
            foreach (string key in changed.get_keys ()) {
                switch (key) {
                    case "DesktopEntry":
                        update_desktop_entry ();
                        break;
                    case "PlaybackStatus":
                    case "CanPause":
                    case "CanPlay":
                        update_button_play_pause (metadata);
                        break;
                    case "Metadata":
                        update_content ();
                        break;
                    case "Shuffle":
                        update_button_shuffle (metadata);
                        break;
                    case "LoopStatus":
                        update_button_repeat (metadata);
                        break;
                    case "CanGoPrevious":
                        update_button_prev (metadata);
                        break;
                    case "CanGoNext":
                        update_button_forward (metadata);
                        break;
                    case "CanControl":
                        update_buttons (metadata);
                        break;
                }
            }
            debug ("Changed: %s", string.joinv (", ", changed.get_keys_as_array ()));
        }

        private void update_content () {
            HashTable<string, Variant> metadata = source.media_player.metadata;

            // Desktop Entry
            update_desktop_entry ();

            // Album art
            update_album_art.begin (metadata);

            // Title
            update_title (metadata);

            // Subtitle
            update_sub_title (metadata);

            // Update the buttons
            update_buttons (metadata);

            // Emit signal
            content_updated ();
        }

        private void update_buttons (HashTable<string, Variant> metadata) {
            // Shuffle check
            update_button_shuffle (metadata);

            // Prev check
            update_button_prev (metadata);

            // Play/Pause
            update_button_play_pause (metadata);

            // Next check
            update_button_forward (metadata);

            // Repeat check
            update_button_repeat (metadata);
        }

        private void update_desktop_entry () {
            Variant ?entry_name = source.get_mpris_prop ("DesktopEntry");
            if (entry_name == null
                || !entry_name.is_of_type (VariantType.STRING)
                || entry_name.get_string () == "") {
                desktop_entry = null;
                return;
            }
            string name = "%s.desktop".printf (entry_name.get_string ());
            desktop_entry = new DesktopAppInfo (name);
        }

        private void update_title (HashTable<string, Variant> metadata) {
            if ("xesam:title" in metadata
                && metadata["xesam:title"].get_string () != "") {
                string str = metadata["xesam:title"].get_string ();
                title.set_text (str);
            } else {
                string ?name = null;
                if (desktop_entry is DesktopAppInfo) {
                    name = desktop_entry.get_display_name ();
                    if (name == "") {
                        name = desktop_entry.get_name ();
                    }
                }
                if (name == null) {
                    name = "Media Player";
                }
                title.set_text (name);
            }
        }

        private void update_sub_title (HashTable<string, Variant> metadata) {
            // Get album
            string ?album = null;
            if ("xesam:album" in metadata
                && metadata["xesam:album"].get_string () != "") {
                album = metadata["xesam:album"].get_string ();
            }

            // Get first artist
            string ?artist = null;
            // Try to get either "artist" or "albumArtist"
            const string[] TYPES = { "xesam:artist", "xesam:albumArtist" };
            foreach (unowned string type in TYPES) {
                if (artist != null && artist.length > 0) {
                    break;
                }
                if (type in metadata
                    && metadata[type].get_type_string () == "as") {
                    VariantIter iter = new VariantIter (metadata[type]);
                    Variant ?value = null;
                    while ((value = iter.next_value ()) != null) {
                        artist = value.get_string ();
                        break;
                    }
                }
            }

            string result = "";
            if (album != null) {
                if (artist != null && artist.length > 0) {
                    result = string.joinv (" - ", { artist, album });
                } else {
                    result = album;
                }
            }
            sub_title.set_text (result);
            // Hide if no album or artist
            sub_title.set_visible (result.length > 0);
        }

        private async void update_album_art (HashTable<string, Variant> metadata) {
            if ("mpris:artUrl" in metadata) {
                string art_data = metadata["mpris:artUrl"].get_string ();
                if (art_data == prev_art_data) {
                    return;
                }
                prev_art_data = art_data;

                debug ("MPRIS album art: \"%s\"", art_data);

                // Cancel previous download, reset the state and download again
                album_art_cancellable.cancel ();
                album_art_cancellable.reset ();

                Gdk.Texture ?album_art_texture = null;
                try {
                    Uri uri = Uri.parse (art_data, UriFlags.NONE);
                    if (uri.get_scheme () == "data") {
                        // Load base64 images
                        art_data = uri.get_path ();
                        int comma_index = art_data.index_of_char (',');
                        if (comma_index < 0) {
                            throw new UriError.FAILED ("Could not parse \"data\" uri");
                        }
                        string meta_data = art_data.substring (0, comma_index);
                        if (!meta_data.contains (";base64")) {
                            throw new UriError.FAILED ("\"data\" uri doesn't contain base64 data");
                        }

                        string data_part = art_data.substring (comma_index + 1);
                        uchar[] data = Base64.decode (data_part);

                        // Try to parse the mime-type if it exists
                        Gdk.PixbufLoader loader;
                        string uri_hints[2] = meta_data.split (";");
                        unowned string mime_type = uri_hints[0];
                        if (mime_type.length > 0 && mime_type in pixbuf_mime_types) {
                            loader = new Gdk.PixbufLoader.with_mime_type (mime_type);
                        } else {
                            loader = new Gdk.PixbufLoader ();
                        }
                        loader.write (data);
                        loader.close ();

                        unowned Gdk.Pixbuf ?pixbuf = loader.get_pixbuf ();
                        if (pixbuf != null) {
                            album_art_texture = Gdk.Texture.for_pixbuf (pixbuf);
                        }
                    } else {
                        // Load as a regular file/URL
                        File file = File.new_for_uri (art_data);
                        InputStream stream = yield file.read_async (Priority.DEFAULT,
                                                                    album_art_cancellable);

                        Gdk.Pixbuf pixbuf = yield new Gdk.Pixbuf.from_stream_async (
                            stream, album_art_cancellable);
                        album_art_texture = Gdk.Texture.for_pixbuf (pixbuf);
                    }
                } catch (Error e) {
                    critical ("MPRIS (%s) album art error: %s. Using fallback...",
                              source.media_player.identity, e.message);
                }
                if (album_art_texture != null) {
                    // Set album art
                    int icon_size = mpris_config.image_size;
                    if (icon_size < 0) {
                        icon_size = album_art_texture.width > album_art_texture.height
                            ? album_art_texture.height : album_art_texture.width;
                    }
                    Gtk.Snapshot snapshot = new Gtk.Snapshot ();
                    Functions.scale_texture (album_art_texture,
                                             icon_size, icon_size,
                                             get_scale_factor (), snapshot);
                    Graphene.Size size = Graphene.Size ().init (icon_size, icon_size);
                    album_art.set_from_paintable (snapshot.free_to_paintable (size));
                    album_art.set_visible (mpris_config.show_album_art != AlbumArtState.NEVER);

                    // Set background album art
                    background_picture.set_paintable (album_art_texture);

                    this.queue_draw ();
                    return;
                }
            }

            album_art.set_visible (mpris_config.show_album_art == AlbumArtState.ALWAYS);

            // Get the app icon
            Icon ?icon = null;
            if (desktop_entry is DesktopAppInfo) {
                icon = desktop_entry.get_icon ();
            }
            unowned Gtk.IconTheme icon_theme = Gtk.IconTheme.get_for_display (get_display ());
            if (icon != null) {
                Gtk.IconPaintable ?icon_info = icon_theme.lookup_by_gicon (
                    icon, 128, get_scale_factor (), Gtk.TextDirection.NONE, 0);
                if (icon_info != null) {
                    album_art.set_from_gicon (icon);
                    background_picture.set_paintable (icon_info);

                    debug ("MPRIS album art: using .desktop icon");
                    return;
                }
            }

            // Default icon
            string icon_name = "audio-x-generic-symbolic";
            album_art.set_from_icon_name (icon_name);
            Gtk.IconPaintable ?icon_info = icon_theme.lookup_icon (
                icon_name, null, 128, get_scale_factor (),
                Gtk.TextDirection.NONE, 0);
            background_picture.set_paintable (icon_info);

            debug ("MPRIS album art: using default icon");
        }

        private void update_button_shuffle (HashTable<string, Variant> metadata) {
            if (!(button_shuffle is Gtk.Widget)) {
                return;
            }

            if (source.media_player.can_control) {
                // Shuffle check
                Variant ?shuffle = source.get_mpris_player_prop ("Shuffle");
                if (shuffle == null || !shuffle.is_of_type (VariantType.BOOLEAN)) {
                    button_shuffle.sensitive = false;
                    button_shuffle.get_child ().opacity = 1;
                    button_shuffle.hide ();
                } else {
                    button_shuffle.sensitive = true;
                    button_shuffle.get_child ().opacity =
                        source.media_player.shuffle ? 1 : UNSELECTED_OPACITY;
                    button_shuffle.show ();
                }
            } else {
                button_shuffle.hide ();
            }
        }

        private void update_button_prev (HashTable<string, Variant> metadata) {
            if (!(button_prev is Gtk.Widget)) {
                return;
            }

            button_prev.set_sensitive (source.media_player.can_go_previous
                                       && source.media_player.can_control);
        }

        private void update_button_play_pause (HashTable<string, Variant> metadata) {
            if (!(button_play_pause is Gtk.Widget)) {
                return;
            }

            string icon_name;
            bool check;
            switch (source.media_player.playback_status) {
                case "Playing" :
                    icon_name = ICON_PAUSE;
                    check = source.media_player.can_pause;
                    break;
                case "Paused" :
                case "Stopped" :
                    default :
                    icon_name = ICON_PLAY;
                    check = source.media_player.can_play;
                    break;
            }
            button_play_pause.set_icon_name (icon_name);
            button_play_pause.sensitive = check && source.media_player.can_control;
        }

        private void update_button_forward (HashTable<string, Variant> metadata) {
            if (!(button_next is Gtk.Widget)) {
                return;
            }

            button_next.set_sensitive (source.media_player.can_go_next
                                       && source.media_player.can_control);
        }

        private void update_button_repeat (HashTable<string, Variant> metadata) {
            if (!(button_repeat is Gtk.Widget)) {
                return;
            }

            if (source.media_player.can_control) {
                // Repeat check
                Variant ?repeat = source.get_mpris_player_prop ("LoopStatus");
                if (repeat == null || !repeat.is_of_type (VariantType.STRING)) {
                    button_repeat.sensitive = false;
                    button_repeat.hide ();
                } else {
                    string icon_name;
                    double opacity = 1.0;
                    bool remove_flat_css_class = true;
                    switch (repeat.get_string ()) {
                        default :
                        case "None" :
                            icon_name = ICON_REPEAT;
                            opacity = UNSELECTED_OPACITY;
                            remove_flat_css_class = false;
                            break;
                        case "Playlist" :
                            icon_name = ICON_REPEAT;
                            break;
                        case "Track" :
                            icon_name = ICON_REPEAT_SONG;
                            break;
                    }
                    if (remove_flat_css_class) {
                        button_repeat.remove_css_class ("flat");
                    } else {
                        button_repeat.add_css_class ("flat");
                    }
                    button_repeat.get_child ().opacity = opacity;
                    button_repeat.sensitive = true;
                    button_repeat.set_icon_name (icon_name);
                    button_repeat.show ();
                }
            } else {
                button_repeat.hide ();
            }
        }
    }
}
07070100000075000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000004600000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/notifications07070100000076000081A4000000000000000000000001690B16EB000045B7000000000000000000000000000000000000005900000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/notifications/notifications.valanamespace SwayNotificationCenter.Widgets {
    [GtkTemplate (ui = "/org/erikreider/swaync/ui/notifications_widget.ui")]
    public class Notifications : BaseWidget {
        public override string widget_name {
            get {
                return "notifications";
            }
        }

        const string STACK_NOTIFICATIONS_PAGE = "notifications-list";
        const string STACK_PLACEHOLDER_PAGE = "notifications-placeholder";

        [GtkChild]
        unowned Gtk.Label text_empty_label;
        [GtkChild]
        unowned Gtk.Stack stack;
        [GtkChild]
        unowned Gtk.ScrolledWindow scrolled_window;
        [GtkChild]
        unowned Gtk.Viewport viewport;
        [GtkChild]
        unowned Gtk.ListBox list_box;

        private IterListBoxController list_box_controller;

        private unowned NotificationGroup ?expanded_group = null;
        private uint scroll_timer_id = 0;

        private HashTable<uint32, unowned NotificationGroup> noti_groups_id =
            new HashTable<uint32, unowned NotificationGroup> (direct_hash, direct_equal);
        /** NOTE: Only includes groups with ids with length of > 0 */
        private HashTable<string, unowned NotificationGroup> noti_groups_name =
            new HashTable<string, unowned NotificationGroup> (str_hash, str_equal);

        private bool list_reverse = false;

        // Default config values
        bool vertical_expand = true;

        public Notifications (SwayncDaemon swaync_daemon, NotiDaemon noti_daemon) {
            base ("", swaync_daemon, noti_daemon);

            list_box_controller = new IterListBoxController (list_box);

            // TODO: Move this into notifications config!
            text_empty_label.set_text (ConfigModel.instance.text_empty);

            stack.set_visible_child_name (STACK_PLACEHOLDER_PAGE);

            list_box.set_valign (Gtk.Align.START);

            reload_config ();
        }

        public override void on_cc_visibility_change (bool value) {
            if (value) {
                focus_first_notification ();
            }
        }

        public void reload_config () {
            Json.Object ?config = get_config (this);
            if (config != null) {
                // Get vexpand
                bool found_vexpand;
                bool ?vexpand = get_prop<bool> (config, "vexpand", out found_vexpand);
                if (found_vexpand) {
                    this.vertical_expand = vexpand;
                }
            }

            set_vexpand (this.vertical_expand);
            scrolled_window.set_propagate_natural_height (!this.vertical_expand);
            stack.set_vhomogeneous (this.vertical_expand);
        }

        public uint notification_count () {
            uint count = 0;
            foreach (unowned Gtk.Widget widget in list_box_controller.get_children ()) {
                if (widget is NotificationGroup) {
                    count += ((NotificationGroup) widget).get_num_notifications ();
                }
            }
            return count;
        }

        public void close_all_notifications () {
            foreach (unowned Gtk.Widget w in list_box_controller.get_children ()) {
                NotificationGroup group = (NotificationGroup) w;
                if (group != null) {
                    group.close_all_notifications ();
                }
            }

            try {
                swaync_daemon.subscribe_v2 (notification_count (),
                                            swaync_daemon.get_dnd (),
                                            noti_daemon.control_center.get_visibility (),
                                            swaync_daemon.inhibited);
            } catch (Error e) {
                stderr.printf (e.message + "\n");
            }

            if (ConfigModel.instance.hide_on_clear) {
                noti_daemon.control_center.set_visibility (false);
            }
        }

        public void close_notification (uint32 id, bool dismiss) {
            unowned NotificationGroup group = null;
            if (!noti_groups_id.lookup_extended (id, null, out group)) {
                return;
            }
            foreach (var w in group.get_notifications ()) {
                var noti = (Notification) w;
                if (noti != null && noti.param.applied_id == id) {
                    if (dismiss) {
                        noti.close_notification (false);
                    }
                    group.remove_notification (noti);
                    noti_groups_id.remove (id);
                    break;
                }
            }

            if (group.only_single_notification ()) {
                if (expanded_group == group) {
                    expanded_group = null;
                }
            } else if (group.is_empty ()) {
                if (group.name_id.length > 0) {
                    noti_groups_name.remove (group.name_id);
                }
                if (expanded_group == group) {
                    expanded_group = null;
                }

                // Make sure to change focus to the sibling. Otherwise,
                // the ListBox focuses the first notification.
                if (list_reverse) {
                    navigate_up (group, true);
                } else {
                    navigate_down (group, true);
                }
                list_box_controller.remove (group);

                // Switches the stack page depending on the amount of notifications
                if (list_box_controller.length < 1) {
                    stack.set_visible_child_name (STACK_PLACEHOLDER_PAGE);
                }
            }
        }

        public void replace_notification (uint32 id, NotifyParams new_params) {
            unowned NotificationGroup group = null;
            if (noti_groups_id.lookup_extended (id, null, out group)) {
                foreach (var w in group.get_notifications ()) {
                    var noti = (Notification) w;
                    if (noti != null && noti.param.applied_id == id) {
                        noti_groups_id.remove (id);
                        noti_groups_id.set (new_params.applied_id, group);
                        noti.replace_notification (new_params);
                        // Position the notification in the beginning of the list
                        list_box.invalidate_sort ();
                        return;
                    }
                }
            }

            // Add a new notification if the old one isn't visible
            add_notification (new_params);
        }

        public void add_notification (NotifyParams param) {
            var noti = new Notification.regular (param,
                                                 noti_daemon,
                                                 NotificationType.CONTROL_CENTER);
            noti.set_time ();

            NotificationGroup ?group = null;
            if (param.name_id.length > 0) {
                noti_groups_name.lookup_extended (param.name_id, null, out group);
            }
            if (group == null || ConfigModel.instance.notification_grouping == false) {
                group = new NotificationGroup (param.name_id, param.display_name);
                // Collapse other groups on expand
                group.on_expand_change.connect ((expanded) => {
                    if (!expanded) {
                        foreach (unowned Gtk.Widget child in list_box_controller.get_children ()) {
                            if (child is NotificationGroup) {
                                child.remove_css_class ("not-expanded");
                            }
                        }
                        expanded_group = null;
                        return;
                    }

                    expanded_group = group;
                    float y = expanded_group.get_relative_y (list_box);
                    if (y > 0) {
                        scroll_animate (y);
                    }
                    foreach (unowned Gtk.Widget child in list_box_controller.get_children ()) {
                        NotificationGroup g = (NotificationGroup) child;
                        if (g != null && g != group) {
                            g.set_expanded (false);
                            child.add_css_class ("not-expanded");
                        }
                    }
                });
                if (param.name_id.length > 0) {
                    noti_groups_name.set (param.name_id, group);
                }

                // Switches the stack page depending on the amount of notifications
                stack.set_visible_child_name (STACK_NOTIFICATIONS_PAGE);

                list_box_controller.append (group);
            }

            // Set the group as not-expanded (reduce opacity) if there's
            // already a group that's expanded.
            if (expanded_group != null) {
                group.add_css_class ("not-expanded");
            }

            noti_groups_id.set (param.applied_id, group);

            group.add_notification (noti);
            list_box.invalidate_sort ();
            scroll_to_start ();
            try {
                swaync_daemon.subscribe_v2 (notification_count (),
                                            swaync_daemon.get_dnd (),
                                            noti_daemon.control_center.get_visibility (),
                                            swaync_daemon.inhibited);
            } catch (Error e) {
                stderr.printf (e.message + "\n");
            }

            // Focus the incoming notification
            group.grab_focus ();
        }

        public void set_list_is_reversed (bool reversed) {
            list_reverse = reversed;
            list_box.set_valign (reversed ? Gtk.Align.END : Gtk.Align.START);

            list_box.set_sort_func (list_box_sort_func);
            list_box.set_selection_mode (Gtk.SelectionMode.NONE);
            list_box.set_activate_on_single_click (false);
        }

        public bool key_press_event_cb (uint keyval, uint keycode, Gdk.ModifierType state) {
            var children = list_box_controller.get_children ();
            if (!(list_box.get_focus_child () is NotificationGroup)) {
                focus_first_notification ();
            }
            var group = (NotificationGroup) list_box.get_focus_child ();
            switch (Gdk.keyval_name (keyval)) {
                case "Return" :
                    if (group != null) {
                        var noti = group.get_latest_notification ();
                        if (group.only_single_notification () && noti != null) {
                            noti.click_default_action ();
                            break;
                        }
                        group.on_expand_change (group.toggle_expanded ());
                    }
                    break;
                case "Delete" :
                case "BackSpace" :
                    if (group != null && !children.is_empty ()) {
                        var noti = group.get_latest_notification ();
                        if (group.only_single_notification () && noti != null) {
                            close_notification (noti.param.applied_id, true);
                            break;
                        }
                        group.close_all_notifications ();
                        break;
                    }
                    break;
                case "C":
                    close_all_notifications ();
                    break;
                case "D":
                    try {
                        swaync_daemon.toggle_dnd ();
                    } catch (Error e) {
                        critical ("Error: %s\n", e.message);
                    }
                    break;
                case "Down":
                    navigate_down (group);
                    break;
                case "Up":
                    navigate_up (group);
                    break;
                case "Home":
                    navigate_to_first_notification ();
                    break;
                case "End":
                    navigate_to_last_notification ();
                    break;
                default:
                    // Pressing 1-9 to activate a notification action
                    for (int i = 0; i < 9; i++) {
                        uint num_keyval = Gdk.keyval_from_name (
                            (i + 1).to_string ());
                        if (keyval == num_keyval && group != null) {
                            var noti = group.get_latest_notification ();
                            noti.click_alt_action (i);
                            break;
                        }
                    }
                    break;
            }
            // Override the builtin list navigation
            return true;
        }

        /**
         * Returns < 0 if row1 should be before row2, 0 if they are equal
         * and > 0 otherwise
         */
        private int list_box_sort_func (Gtk.ListBoxRow row1, Gtk.ListBoxRow row2) {
            int val = list_reverse ? 1 : -1;

            var a_group = (NotificationGroup) row1;
            var b_group = (NotificationGroup) row2;

            // Check urgency before time
            var a_urgency = a_group.get_is_urgent ();
            var b_urgency = b_group.get_is_urgent ();
            if (a_urgency != b_urgency) {
                return a_urgency ? val : val * -1;
            }

            // Check time
            var a_time = a_group.get_time ();
            var b_time = b_group.get_time ();
            if (a_time < 0 || b_time < 0) {
                return 0;
            }
            // Sort the list in reverse if needed
            if (a_time == b_time) {
                return 0;
            }
            return a_time > b_time ? val : val * -1;
        }

        // Scroll to the expanded group once said group has fully expanded
        private void scroll_animate (double to) {
            if (scroll_timer_id > 0) {
                Source.remove (scroll_timer_id);
                scroll_timer_id = 0;
            }
            scroll_timer_id = Timeout.add_once (Constants.ANIMATION_DURATION, () => {
                scroll_timer_id = 0;
                if (expanded_group == null) {
                    return;
                }
                float y = expanded_group.get_relative_y (list_box);
                if (y > 0) {
                    viewport.scroll_to (expanded_group, null);
                }
            });
        }

        private void scroll_to_start () {
            Gtk.ScrollType scroll_type = Gtk.ScrollType.START;
            if (list_reverse) {
                scroll_type = Gtk.ScrollType.END;
            }
            scrolled_window.scroll_child (scroll_type, false);
        }

        private void navigate_list (int i) {
            if (list_box_controller.length == 0) {
                return;
            }

            unowned Gtk.ListBoxRow ?widget = list_box.get_row_at_index (i);
            if (widget == null) {
                // Try getting the last widget
                if (list_reverse) {
                    widget = list_box.get_row_at_index (0);
                } else {
                    int len = list_box_controller.length - 1;
                    widget = list_box.get_row_at_index (len);
                }
            }
            if (widget != null) {
                widget.grab_focus ();
            } else {
                list_box.grab_focus ();
            }
        }

        private void navigate_up (NotificationGroup ?focused_group,
                                  bool fallback_other_dir = false) {
            if (list_box_controller.length == 1) {
                focus_first_notification ();
                return;
            }
            if (!(focused_group is NotificationGroup) || list_box_controller.length == 0) {
                return;
            }
            if (list_box.get_first_child () == focused_group) {
                if (fallback_other_dir) {
                    navigate_down (focused_group, false);
                }
                return;
            }

            focused_group.move_focus (Gtk.DirectionType.TAB_BACKWARD);
        }

        private void navigate_down (NotificationGroup ?focused_group,
                                    bool fallback_other_dir = false) {
            if (list_box_controller.length == 1) {
                focus_first_notification ();
                return;
            }
            if (!(focused_group is NotificationGroup) || list_box_controller.length == 0) {
                return;
            }
            if (list_box.get_last_child () == focused_group) {
                if (fallback_other_dir) {
                    navigate_up (focused_group, false);
                }
                return;
            }

            focused_group.move_focus (Gtk.DirectionType.TAB_FORWARD);
        }

        private void navigate_to_first_notification () {
            int i = (list_reverse ? list_box_controller.length - 1 : 0)
                 .clamp (0, list_box_controller.length);
            navigate_list (i);
        }

        private void navigate_to_last_notification () {
            int i = (list_reverse ? 0 : list_box_controller.length - 1)
                 .clamp (0, list_box_controller.length);
            navigate_list (i);
        }

        private void focus_first_notification () {
            navigate_to_first_notification ();

            foreach (unowned Gtk.Widget w in list_box_controller.get_children ()) {
                var group = (NotificationGroup) w;
                if (group != null) {
                    group.update ();
                }
            }
        }
    }
}
07070100000077000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000003F00000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/shared07070100000078000081A4000000000000000000000001690B16EB00000711000000000000000000000000000000000000005100000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/shared/toggleButton.valanamespace SwayNotificationCenter.Widgets {
    class ToggleButton : Gtk.ToggleButton {
        private string command;
        private string update_command;
        private ulong handler_id;

        public ToggleButton (string label, string command, string update_command, bool active) {
            this.command = command;
            this.update_command = update_command;
            this.label = label;

            this.set_has_frame (true);

            if (active) {
                this.active = true;
            }

            this.handler_id = this.toggled.connect (on_toggle);
        }

        private async void on_toggle () {
            string msg = "";
            string[] env_additions = { "SWAYNC_TOGGLE_STATE=" + this.active.to_string () };
            yield Functions.execute_command (this.command, env_additions, out msg);
        }

        public async void on_update () {
            if (update_command == "") {
                return;
            }
            string msg = "";
            string[] env_additions = { "SWAYNC_TOGGLE_STATE=" + this.active.to_string () };
            yield Functions.execute_command (this.update_command, env_additions, out msg);

            try {
                // remove trailing whitespaces
                Regex regex = new Regex ("\\s+$");
                string res = regex.replace (msg, msg.length, 0, "");
                GLib.SignalHandler.block (this, this.handler_id);
                if (res.up () == "TRUE") {
                    this.active = true;
                } else {
                    this.active = false;
                }
                GLib.SignalHandler.unblock (this, this.handler_id);
            } catch (RegexError e) {
                stderr.printf ("RegexError: %s\n", e.message);
            }
        }
    }
}
07070100000079000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000003F00000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/slider0707010000007A000081A4000000000000000000000001690B16EB00001286000000000000000000000000000000000000004B00000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/slider/slider.valanamespace SwayNotificationCenter.Widgets {
    public class Slider : BaseWidget {
        public override string widget_name {
            get {
                return "slider";
            }
        }

        Gtk.Label label_widget = new Gtk.Label (null);
        Gtk.Scale slider = new Gtk.Scale.with_range (Gtk.Orientation.HORIZONTAL, 0, 100, 1);

        private double min_limit;
        private double max_limit;
        private double ?last_set;
        private bool cmd_ing = false;

        private string cmd_setter;
        private string cmd_getter;

        public Slider (string suffix, SwayncDaemon swaync_daemon, NotiDaemon noti_daemon) {
            base (suffix, swaync_daemon, noti_daemon);

            int ?round_digits = 0;

            Json.Object ?config = get_config (this);
            if (config != null) {
                string ?label = get_prop<string> (config, "label");
                label_widget.set_label (label ?? "Slider");

                cmd_setter = get_prop<string> (config, "cmd_setter") ?? "";
                cmd_getter = get_prop<string> (config, "cmd_getter") ?? "";

                int ?min = get_prop<int> (config, "min");
                int ?max = get_prop<int> (config, "max");
                int ?maxl = get_prop<int> (config, "max_limit");
                int ?minl = get_prop<int> (config, "min_limit");
                round_digits = get_prop<int> (config, "value_scale");

                if (min == null) {
                    min = 0;
                }
                if (max == null) {
                    max = 100;
                }
                if (round_digits == null) {
                    round_digits = 0;
                }

                max_limit = maxl != null ?double.min (max, maxl) : max;

                min_limit = minl != null ?double.max (min, minl) : min;

                double scale = Math.pow (10, round_digits);

                min_limit /= scale;
                max_limit /= scale;

                slider.set_range (min / scale, max / scale);
            }

            slider.set_draw_value (false);
            slider.set_round_digits (round_digits);
            slider.set_hexpand (true);
            slider.set_halign (Gtk.Align.FILL);
            slider.value_changed.connect (() => {
                double value = slider.get_value ();
                if (value > max_limit) {
                    value = max_limit;
                }
                if (value < min_limit) {
                    value = min_limit;
                }

                slider.set_value (value);
                slider.tooltip_text = value.to_string ();

                if (cmd_setter != "" && last_set != value) {
                    last_set = value;
                    queue_set.begin (value);
                }
            });

            append (label_widget);
            append (slider);
        }

        public string expand_cmd (string cmd, string regex, string value) {
            try {
                Regex _regx = new Regex (Regex.escape_string (regex));
                return _regx.replace_literal (cmd, -1, 0, value);
            } catch (Error e) {
                warning ("failed to expand cmd: %s\n", e.message);
                return cmd;
            }
        }

        public async void queue_set (double value) {
            if (cmd_ing) {
                return;
            }

            cmd_ing = true;
            var cmd = expand_cmd (cmd_setter, "$value", value.to_string ());
            yield Functions.execute_command (cmd, {}, null);

            cmd_ing = false;

            // make sure the last_set is applied
            if (value != last_set) {
                yield queue_set (last_set);
            }
        }

        public async void queue_get (int retry, out string value) {
            if (cmd_ing) {
                if (retry <= 0) {
                    value = "";
                    return;
                }
                yield queue_get (retry - 1, out value);
            }

            cmd_ing = true;
            yield Functions.execute_command (cmd_getter, {}, out value);

            cmd_ing = false;
        }

        public async void on_update () {
            if (cmd_getter == "") {
                return;
            }

            string value_str = "";
            yield queue_get (4, out value_str);

            double value = double.parse (value_str);
            if (value <= max_limit && value >= min_limit) {
                last_set = value;
                slider.set_value (value);
            }
        }

        public override void on_cc_visibility_change (bool value) {
            if (value) {
                on_update.begin ();
            }
        }
    }
}
0707010000007B000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000003E00000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/title0707010000007C000081A4000000000000000000000001690B16EB00000AA8000000000000000000000000000000000000004900000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/title/title.valanamespace SwayNotificationCenter.Widgets {
    public class Title : BaseWidget {
        public override string widget_name {
            get {
                return "title";
            }
        }

        Gtk.Label title_widget;
        Gtk.Button clear_all_button;

        // Default config values
        string title = "Notifications";
        bool has_clear_all_button = true;
        string button_text = "Clear All";

        public Title (string suffix, SwayncDaemon swaync_daemon, NotiDaemon noti_daemon) {
            base (suffix, swaync_daemon, noti_daemon);

            Json.Object ?config = get_config (this);
            if (config != null) {
                // Get title
                string ?title = get_prop<string> (config, "text");
                if (title != null) {
                    this.title = title;
                }
                // Get has clear-all-button
                bool found_clear_all;
                bool ?has_clear_all_button = get_prop<bool> (
                    config, "clear-all-button", out found_clear_all);
                if (found_clear_all) {
                    this.has_clear_all_button = has_clear_all_button;
                }
                // Get button text
                string ?button_text = get_prop<string> (config, "button-text");
                if (button_text != null) {
                    this.button_text = button_text;
                }
            }

            title_widget = new Gtk.Label (title);
            title_widget.set_hexpand (true);
            title_widget.set_halign (Gtk.Align.START);
            append (title_widget);

            if (has_clear_all_button) {
                clear_all_button = new Gtk.Button.with_label (button_text);
                clear_all_button.clicked.connect (() => {
                    try {
                        swaync_daemon.close_all_notifications ();
                    } catch (Error e) {
                        critical ("Error: %s\n", e.message);
                    }
                });
                if (noti_daemon.control_center != null) {
                    clear_all_button.set_sensitive (
                        noti_daemon.control_center.notification_count () > 0);
                }
                swaync_daemon.subscribe_v2.connect ((count) => {
                    clear_all_button.set_sensitive (count > 0);
                });
                clear_all_button.set_can_focus (false);
                clear_all_button.valign = Gtk.Align.CENTER;
                // Backwards compatible towards older CSS stylesheets
                clear_all_button.add_css_class ("control-center-clear-all");
                append (clear_all_button);
            }
        }
    }
}
0707010000007D000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000003F00000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/volume0707010000007E000081A4000000000000000000000001690B16EB000069D9000000000000000000000000000000000000005000000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/volume/pulseDaemon.vala// From SwaySettings PulseAudio page: https://github.com/ErikReider/SwaySettings/blob/407c9e99dd3e50a0f09c64d94a9e6ff741488378/src/Pages/Pulse/PulseDaemon.vala
using Gee;
using PulseAudio;

namespace SwayNotificationCenter.Widgets {
    /**
     * Loosely based off of Elementary OS switchboard-plug-sound
     * https://github.com/elementary/switchboard-plug-sound
     */
    public class PulseDaemon : Object {
        private Context ?context;
        private GLibMainLoop mainloop;
        private bool quitting = false;

        public bool running { get; private set; }

        private string default_sink_name { get; private set; }
        private string default_source_name { get; private set; }

        private PulseDevice ?default_sink = null;

        public HashMap<string, PulseDevice> sinks { get; private set; }

        public HashMap<uint32, PulseSinkInput> active_sinks { get; private set; }

        construct {
            mainloop = new GLibMainLoop ();

            sinks = new HashMap<string, PulseDevice> ();

            active_sinks = new HashMap<uint32, PulseSinkInput> ();
        }

        public void start () {
            get_context ();
        }

        public void close () {
            quitting = true;
            if (this.context != null) {
                context.disconnect ();
                context = null;
            }
        }

        ~PulseDaemon () {
            this.close ();
        }

        public signal void change_default_device (PulseDevice device);

        public signal void new_active_sink (PulseSinkInput device);
        public signal void change_active_sink (PulseSinkInput device);
        public signal void remove_active_sink (PulseSinkInput device);

        public signal void new_device (PulseDevice device);
        public signal void change_device (PulseDevice device);
        public signal void remove_device (PulseDevice device);

        private void get_context () {
            var ctx = new Context (mainloop.get_api (), null);
            ctx.set_state_callback ((ctx) => {
                debug ("Pulse Status: %s\n", ctx.get_state ().to_string ());
                switch (ctx.get_state ()) {
                    case Context.State.CONNECTING :
                    case Context.State.AUTHORIZING :
                    case Context.State.SETTING_NAME:
                        break;
                    case Context.State.READY:
                        ctx.set_subscribe_callback (subscription);
                        ctx.subscribe (Context.SubscriptionMask.SINK_INPUT |
                                       Context.SubscriptionMask.SINK |
                                       Context.SubscriptionMask.CARD |
                                       Context.SubscriptionMask.SERVER);
                        // Init data
                        ctx.get_server_info (this.get_server_info);
                        running = true;
                        break;
                    case Context.State.TERMINATED:
                    case Context.State.FAILED:
                        running = false;
                        if (quitting) {
                            quitting = false;
                            break;
                        }
                        stderr.printf (
                            "PulseAudio connection lost. Will retry connection.\n");
                        get_context ();
                        break;
                    default:
                        running = false;
                        stderr.printf ("Connection failure: %s\n",
                                       PulseAudio.strerror (ctx.errno ()));
                        break;
                }
            });
            if (ctx.connect (
                    null, Context.Flags.NOFAIL, null) < 0) {
                stdout.printf ("pa_context_connect() failed: %s\n",
                               PulseAudio.strerror (ctx.errno ()));
            }
            this.context = ctx;
        }

        private void subscription (Context ctx,
                                   Context.SubscriptionEventType t,
                                   uint32 index) {
            var type = t & Context.SubscriptionEventType.FACILITY_MASK;
            var event = t & Context.SubscriptionEventType.TYPE_MASK;
            switch (type) {
                case Context.SubscriptionEventType.SINK_INPUT:
                    switch (event) {
                        default: break;
                        case Context.SubscriptionEventType.NEW:
                        case Context.SubscriptionEventType.CHANGE:
                            ctx.get_sink_input_info_list (this.get_sink_input_info);
                            break;
                        case Context.SubscriptionEventType.REMOVE:
                            // A safe way of removing the sink_input
                            var iter = active_sinks.map_iterator ();
                            while (iter.next ()) {
                                var sink_input = iter.get_value ();
                                if (sink_input.index != index) {
                                    continue;
                                }
                                this.remove_active_sink (sink_input);
                                iter.unset ();
                                break;
                            }
                            break;
                    }
                    break;
                case Context.SubscriptionEventType.SINK:
                    switch (event) {
                        default: break;
                        case Context.SubscriptionEventType.NEW:
                        case Context.SubscriptionEventType.CHANGE:
                            ctx.get_sink_info_by_index (index, this.get_sink_info);
                            break;
                        case Context.SubscriptionEventType.REMOVE:
                            foreach (var sink in sinks.values) {
                                if (sink.device_index != index) {
                                    continue;
                                }
                                sink.removed = true;
                                sink.is_default = false;
                                this.remove_device (sink);
                                break;
                            }
                            break;
                    }
                    break;
                case Context.SubscriptionEventType.CARD:
                    switch (event) {
                        default: break;
                        case Context.SubscriptionEventType.NEW:
                        case Context.SubscriptionEventType.CHANGE:
                            ctx.get_card_info_by_index (index, this.get_card_info);
                            break;
                        case Context.SubscriptionEventType.REMOVE:
                            // A safe way of removing the sink_input
                            var iter = sinks.map_iterator ();
                            while (iter.next ()) {
                                var device = iter.get_value ();
                                if (device.card_index != index) {
                                    continue;
                                }
                                device.removed = true;
                                device.is_default = false;
                                iter.unset ();
                                this.remove_device (device);
                                break;
                            }
                            break;
                    }
                    break;
                case Context.SubscriptionEventType.SERVER:
                    ctx.get_server_info (this.get_server_info);
                    break;
                default: break;
            }
        }

        /*
         * Getters
         */

        /**
         * Gets called when any server value changes like default devices
         * Calls `get_card_info_list` and `get_sink_info_list`
         */
        private void get_server_info (Context ctx, ServerInfo ?info) {
            if (this.default_sink_name == null) {
                this.default_sink_name = info.default_sink_name;
            }
            if (this.default_sink_name != info.default_sink_name) {
                this.default_sink_name = info.default_sink_name;
            }

            ctx.get_card_info_list (this.get_card_info);
            ctx.get_sink_info_list (this.get_sink_info);

            foreach (var sink_input in active_sinks) {
                sink_input.value.active = false;
            }
            ctx.get_sink_input_info_list (this.get_sink_input_info);
            var iter = active_sinks.map_iterator ();
            while (iter.next ()) {
                var sink_input = iter.get_value ();
                if (!sink_input.active) {
                    this.remove_active_sink (sink_input);
                    iter.unset ();
                }
            }
        }

        private void get_sink_input_info (Context ctx, SinkInputInfo ?info, int eol) {
            if (info == null || eol != 0) {
                return;
            }

            uint32 id = PulseSinkInput.get_hash_map_key (info.index);
            PulseSinkInput sink_input = null;
            bool has_sink_input = active_sinks.has_key (id);
            if (has_sink_input) {
                sink_input = active_sinks.get (id);
            } else {
                sink_input = new PulseSinkInput ();
            }

            sink_input.index = info.index;
            sink_input.sink_index = info.sink;
            sink_input.client_index = info.client;

            sink_input.name = info.proplist.gets ("application.name");
            sink_input.application_binary = info.proplist
                 .gets ("application.process.binary");
            sink_input.application_icon_name = info.proplist
                 .gets ("application.icon_name");
            sink_input.media_name = info.proplist.gets ("media.name");

            sink_input.is_muted = info.mute == 1;

            sink_input.cvolume = info.volume;
            sink_input.channel_map = info.channel_map;
            sink_input.balance = sink_input.cvolume
                 .get_balance (sink_input.channel_map);
            sink_input.volume_operations.foreach ((op) => {
                if (op.get_state () != Operation.State.RUNNING) {
                    sink_input.volume_operations.remove (op);
                }
                return Source.CONTINUE;
            });
            if (sink_input.volume_operations.is_empty) {
                sink_input.volume = volume_to_double (
                    sink_input.cvolume.max ());
            }
            sink_input.active = true;

            if (!has_sink_input) {
                active_sinks.set (id, sink_input);
                this.new_active_sink (sink_input);
            } else {
                this.change_active_sink (sink_input);
            }
        }

        private void get_card_info (Context ctx, CardInfo ?info, int eol) {
            if (info == null || eol != 0) {
                return;
            }

            unowned string ?description = info.proplist
                 .gets ("device.description");
            unowned string ?props_icon = info.proplist
                 .gets ("device.icon_name");

            PulseDevice[] ports = {};
            foreach (var port in info.ports) {
                if (port->available == PortAvailable.NO) {
                    continue;
                }

                bool is_input = port->direction == Direction.INPUT;
                HashMap<string, PulseDevice> devices = this.sinks;
                string id = PulseDevice.get_hash_map_key (
                    description, port.name);

                bool has_device = devices.has_key (id);
                PulseDevice device = has_device
                    ? devices.get (id) : new PulseDevice ();
                bool device_is_removed = device.removed;
                device.removed = false;

                device.is_bluetooth = info.proplist.gets ("device.api") == "bluez5";

                device.card_index = info.index;
                device.direction = port.direction;

                device.card_name = info.name;
                device.card_description = description;
                device.card_active_profile = info.active_profile2->name;

                device.port_name = port.name;
                device.port_description = port.description;
                device.port_id = port->proplist.gets ("card.profile.port");

                // Get port profiles2 (profiles is "Superseded by profiles2")
                // and sort largest priority first
                var profiles = new ArrayList<unowned CardProfileInfo2 *>
                     .wrap (port->profiles2);

                profiles.sort ((a, b) => {
                    if (a->priority == b->priority) {
                        return 0;
                    }
                    return a.priority > b.priority ? -1 : 1;
                });
                string[] new_profiles = {};
                Array<PulseCardProfile> pulse_profiles = new Array<PulseCardProfile> ();
                foreach (var profile in profiles) {
                    new_profiles += profile->name;

                    var card_profile = new PulseCardProfile (profile);
                    pulse_profiles.append_val (card_profile);
                    if (profile->name == device.card_active_profile) {
                        device.active_profile = card_profile;
                    }
                }
                device.port_profiles = new_profiles;
                device.profiles = pulse_profiles;

                device.icon_name = port->proplist.gets ("device.icon_name")
                    ?? props_icon;
                if (device.icon_name == null) {
                    device.icon_name = is_input
                        ? "microphone-sensitivity-high"
                                       : "audio-speakers";
                }
                devices.set (id, device);
                ports += device;
                if (!has_device || device_is_removed) {
                    this.new_device (device);
                }
            }

            /** Removes ports that are no longer available */
            var iter = sinks.map_iterator ();
            while (iter.next ()) {
                var device = iter.get_value ();
                if (device.card_index != info.index) {
                    continue;
                }
                bool found = false;
                foreach (var p in ports) {
                    if (device.get_current_hash_key ()
                        == p.get_current_hash_key ()) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    iter.unset ();
                    remove_device (device);
                    break;
                }
            }
        }

        private void get_sink_info (Context ctx, SinkInfo ?info, int eol) {
            if (info == null || eol != 0) {
                return;
            }

            bool found = false;
            foreach (PulseDevice device in sinks.values) {
                if (device.card_index == info.card) {
                    // Sets the name and index to profiles that aren't active
                    // Ex: The HDMI audio port that's not active
                    device.device_name = info.name;
                    device.device_description = info.description;
                    device.device_index = info.index;
                    // If the current selected sink profile is this
                    if (info.active_port != null
                        && info.active_port->name == device.port_name) {
                        found = true;

                        device.card_sink_port_name = info.active_port->name;
                        bool is_default =
                            device.device_name == this.default_sink_name;
                        device.is_default = is_default;

                        device.is_muted = info.mute == 1;

                        device.is_virtual = info.proplist.gets ("node.virtual") == "true";

                        device.cvolume = info.volume;
                        device.channel_map = info.channel_map;
                        device.balance = device.cvolume
                             .get_balance (device.channel_map);
                        device.volume_operations.foreach ((op) => {
                            if (op.get_state () != Operation.State.RUNNING) {
                                device.volume_operations.remove (op);
                            }
                            return Source.CONTINUE;
                        });
                        if (device.volume_operations.is_empty) {
                            device.volume = volume_to_double (
                                device.cvolume.max ());
                        }

                        if (is_default) {
                            this.default_sink = device;
                            this.change_default_device (device);
                        }
                    }
                    this.change_device (device);
                }
            }
            // If not found, it's a cardless device
            if (found) {
                return;
            }

            HashMap<string, PulseDevice> devices = this.sinks;
            string id = PulseDevice.get_hash_map_key (
                info.index.to_string (), info.description);
            bool has_device = devices.has_key (id);
            PulseDevice device = has_device ? devices.get (id) : new PulseDevice ();

            bool device_is_removed = device.removed;
            device.removed = false;

            device.has_card = false;

            device.direction = PulseAudio.Direction.OUTPUT;

            device.device_name = info.name;
            device.device_description = info.description;
            device.device_index = info.index;

            bool is_default = device.device_name == this.default_source_name;
            device.is_default = is_default;

            device.is_muted = info.mute == 1;

            device.is_virtual = info.proplist.gets ("node.virtual") == "true";

            device.icon_name = "application-x-executable-symbolic";

            device.cvolume = info.volume;
            device.channel_map = info.channel_map;
            device.balance = device.cvolume
                 .get_balance (device.channel_map);
            device.volume_operations.foreach ((op) => {
                if (op.get_state () != Operation.State.RUNNING) {
                    device.volume_operations.remove (op);
                }
                return Source.CONTINUE;
            });
            if (device.volume_operations.is_empty) {
                device.volume = volume_to_double (
                    device.cvolume.max ());
            }

            devices.set (id, device);

            if (is_default) {
                this.default_sink = device;
                this.change_default_device (device);
            }
            if (!has_device || device_is_removed) {
                this.new_device (device);
            }
            this.change_device (device);
        }

        /*
         * Setters
         */
        public void set_sink_input_volume (PulseSinkInput sink_input, double volume) {
            sink_input.volume_operations.foreach ((operation) => {
                if (operation.get_state () == Operation.State.RUNNING) {
                    operation.cancel ();
                }

                sink_input.volume_operations.remove (operation);
                return GLib.Source.CONTINUE;
            });

            var cvol = sink_input.cvolume;
            cvol.scale (double_to_volume (volume));
            Operation ?operation = null;
            operation = context.set_sink_input_volume (
                sink_input.index, cvol);
            if (operation != null) {
                sink_input.volume_operations.add (operation);
            }
        }

        public void set_device_volume (PulseDevice device, double volume) {
            device.volume_operations.foreach ((operation) => {
                if (operation.get_state () == Operation.State.RUNNING) {
                    operation.cancel ();
                }

                device.volume_operations.remove (operation);
                return GLib.Source.CONTINUE;
            });

            var cvol = device.cvolume;
            cvol.scale (double_to_volume (volume));
            Operation ?operation = null;
            if (device.direction == Direction.OUTPUT) {
                operation = context.set_sink_volume_by_name (
                    device.device_name, cvol);
            }

            if (operation != null) {
                device.volume_operations.add (operation);
            }
        }

        public async void set_default_device (PulseDevice device) {
            if (device == null) {
                return;
            }
            bool is_input = device.direction == Direction.INPUT;

            // Only set port and card profile if the device is attached to a card
            if (device.has_card) {
                // Gets the profile that includes support for your other device
                string profile_name = device.port_profiles[0];
                PulseDevice alt_device = default_sink;
                if (alt_device != null) {
                    foreach (var profile in device.port_profiles) {
                        if (profile in alt_device.port_profiles) {
                            profile_name = profile;
                            break;
                        }
                    }
                }

                if (profile_name != device.card_active_profile) {
                    yield set_card_profile_by_index (profile_name, device);
                    yield wait_for_update<string> (device, "device-name");
                }

                if (!is_input) {
                    if (device.port_name != device.card_sink_port_name) {
                        debug ("Setting port to: %s", device.port_name);
                        yield set_sink_port_by_name (device);
                    }
                }

                if (device.device_name == null) {
                    yield wait_for_update<string> (device, "device-name");
                }
            }

            if (!is_input) {
                if (device.device_name != default_sink_name) {
                    debug ("Setting default sink to: %s", device.device_name);
                    yield set_default_sink (device);
                }
            }
        }

        private async void wait_for_update<T> (PulseDevice device,
                                               string prop_name) {
            SourceFunc callback = wait_for_update.callback;
            ulong handler_id = 0;
            handler_id = device.notify[prop_name].connect ((s, p) => {
                T prop_value;
                device.get (prop_name, out prop_value);
                if (prop_value != null) {
                    device.disconnect (handler_id);
                    Idle.add ((owned) callback);
                }
            });
            yield;
        }

        public async void set_bluetooth_card_profile (PulseCardProfile profile,
                                                      PulseDevice device) {
            context.set_card_profile_by_index (device.card_index,
                                               profile.name,
                                               (c, success) => {
                if (success == 1) {
                    set_bluetooth_card_profile.callback ();
                } else {
                    stderr.printf ("setting the card %s profile to %s failed\n",
                                   device.card_name, profile.name);
                }
            });
            yield;
            // Wait until the device has been updated
            yield wait_for_update<string> (device, "device-name");
        }

        private async void set_card_profile_by_index (string profile_name,
                                                      PulseDevice device) {
            context.set_card_profile_by_index (device.card_index,
                                               profile_name,
                                               (c, success) => {
                if (success == 1) {
                    set_card_profile_by_index.callback ();
                } else {
                    stderr.printf ("setting the card %s profile to %s failed\n",
                                   device.card_name, profile_name);
                }
            });
            yield;
        }

        private async void set_sink_port_by_name (PulseDevice device) {
            context.set_sink_port_by_name (device.device_name,
                                           device.port_name,
                                           (c, success) => {
                if (success == 1) {
                    set_sink_port_by_name.callback ();
                } else {
                    stderr.printf ("setting sink port to %s failed\n",
                                   device.port_name);
                }
            });
            yield;
        }

        private async void set_default_sink (PulseDevice device) {
            context.set_default_sink (device.device_name, (c, success) => {
                if (success == 1) {
                    set_default_sink.callback ();
                } else {
                    stderr.printf ("setting default sink to %s failed\n",
                                   device.device_name);
                }
            });
            yield;
        }

        public void set_device_mute (bool state, PulseDevice device) {
            if (device.is_muted == state) {
                return;
            }
            if (device.direction == Direction.OUTPUT) {
                context.set_sink_mute_by_index (
                    device.device_index, state);
            }
        }

        public void set_sink_input_mute (bool state, PulseSinkInput sink_input) {
            if (sink_input.is_muted == state) {
                return;
            }
            context.set_sink_input_mute (sink_input.index, state);
        }

        /*
         * Volume utils
         */

        private static double volume_to_double (PulseAudio.Volume vol) {
            double tmp = (double) (vol - PulseAudio.Volume.MUTED);
            return 100 * tmp / (double) (PulseAudio.Volume.NORM - PulseAudio.Volume.MUTED);
        }

        private static PulseAudio.Volume double_to_volume (double vol) {
            double tmp = (double) (PulseAudio.Volume.NORM - PulseAudio.Volume.MUTED) * vol / 100;
            return (PulseAudio.Volume) tmp + PulseAudio.Volume.MUTED;
        }
    }
}
0707010000007F000081A4000000000000000000000001690B16EB0000141D000000000000000000000000000000000000005000000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/volume/pulseDevice.vala// From SwaySettings PulseAudio page: https://github.com/ErikReider/SwaySettings/blob/407c9e99dd3e50a0f09c64d94a9e6ff741488378/src/Pages/Pulse/PulseDevice.vala
using Gee;
using PulseAudio;

namespace SwayNotificationCenter.Widgets {
    public class PulseCardProfile : Object {
        public string name;
        public string description;
        public uint32 n_sinks;
        public uint32 priority;
        int available;

        public PulseCardProfile (CardProfileInfo2 *profile) {
            this.name = profile->name;
            this.description = profile->description;
            this.n_sinks = profile->n_sinks;
            this.priority = profile->priority;
            this.available = profile->available;
        }

        public bool cmp (PulseCardProfile profile) {
            return profile.name == name
                   && profile.description == description
                   && profile.n_sinks == n_sinks
                   && profile.priority == priority
                   && profile.available == available;
        }
    }

    public class PulseDevice : Object {
        public bool removed { get; set; default = false; }

        public bool has_card { get; set; default = true; }

        /** The card index: ex. `Card #49` */
        public uint32 card_index { get; set; }
        /** Sink index: ex. `Sink #55` */
        public uint32 device_index { get; set; }

        /** Input or Output */
        public Direction direction { get; set; }

        /** Is default Sink */
        public bool is_default { get; set; }
        /** If the device is virtual */
        public bool is_virtual { get; set; default = false; }
        /** If the device is a bluetooth device */
        public bool is_bluetooth { get; set; default = false; }

        /** The icon name: `device.icon_name` */
        public string icon_name { get; set; }

        /** The card name: `Name` */
        public string card_name { get; set; }
        /** The card description: `device.description` */
        public string card_description { get; set; }
        /** The card active profile: `Active Profile` */
        public string card_active_profile { get; set; }
        /** The card sink port name: `Active Port` */
        public string card_sink_port_name { get; set; }

        /** The Sink name: `Name` */
        public string ?device_name { get; set; }
        /** The Sink description: `Description` */
        public string device_description { get; set; }
        /** If the Sink is muted: `Mute` */
        public bool is_muted { get; set; }

        public double volume { get; set; }
        public float balance { get; set; default = 0; }
        public CVolume cvolume;
        public ChannelMap channel_map;
        public LinkedList<Operation> volume_operations { get; set; }

        /** Gets the name to be shown to the user:
         * "port_description - card_description"
         */
        public string ?get_display_name () {
            if (card_name == null) {
                return device_description;
            }
            string p_desc = port_description;
            string c_desc = card_description;
            return "%s - %s".printf (p_desc, c_desc);
        }

        /** Compares PulseDevices. Returns true if they're the same */
        public bool cmp (PulseDevice device) {
            return device.card_index == card_index
                   && device.device_index == device_index
                   && device.device_name == device_name
                   && device.device_description == device_description
                   && device.is_default == is_default
                   && device.removed == removed
                   && device.card_active_profile == card_active_profile
                   && device.port_name == port_name;
        }

        /**
         * Gets the name to be shown to the user:
         * If has card: "card_description:port_name"
         * If cardless: "device_index:device_description"
         */
        public string get_current_hash_key () {
            if (card_name == null) {
                return get_hash_map_key (device_index.to_string (),
                                         device_description);
            }
            return get_hash_map_key (card_description, port_name);
        }

        /** Gets the name to be shown to the user:
         * "card_description:port_name"
         */
        public static string get_hash_map_key (string c_desc, string p_name) {
            return string.joinv (":", new string[] { c_desc, p_name });
        }

        /** The port name: `Name` */
        public string port_name { get; set; }
        /** The port name: `Description` */
        public string port_description { get; set; }
        /** The port name: `card.profile.port` */
        public string port_id { get; set; }
        /** All port profiles */
        public string[] port_profiles { get; set; }
        public Array<PulseCardProfile> profiles { get; set; }
        public PulseCardProfile ?active_profile { get; set; }

        construct {
            volume_operations = new LinkedList<Operation> ();
        }
    }
}
07070100000080000081A4000000000000000000000001690B16EB00000876000000000000000000000000000000000000005300000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/volume/pulseSinkInput.vala// From SwaySettings PulseAudio page: https://github.com/ErikReider/SwaySettings/blob/2b05776bce2fd55933a7fbdec995f54849e39e7d/src/Pages/Pulse/PulseSinkInput.vala
using Gee;
using PulseAudio;

namespace SwayNotificationCenter.Widgets {
    public class PulseSinkInput : Object {
        /** The card index: ex. `Sink Input #227` */
        public uint32 index;
        /** The sink index: ex. `55` */
        public uint32 sink_index;
        /** The client index: ex. `266` */
        public uint32 client_index;

        /** The name of the application: `application.name` */
        public string name;
        /** The name of the application binary: `application.process.binary` */
        public string application_binary;
        /** The application icon. Can be null: `application.icon_name` */
        public string ?application_icon_name;
        /** The name of the media: `media.name` */
        public string media_name;

        /** The mute state: `Mute` */
        public bool is_muted;

        public double volume;
        public float balance { get; set; default = 0; }
        public CVolume cvolume;
        public ChannelMap channel_map;
        public LinkedList<Operation> volume_operations;

        public bool active;

        /** Gets the name to be shown to the user:
         * "application_name"
         */
        public string ?get_display_name () {
            return name;
        }

        public bool cmp (PulseSinkInput sink_input) {
            return sink_input.index == index
                   && sink_input.sink_index == sink_index
                   && sink_input.client_index == client_index
                   && sink_input.name == name
                   && sink_input.application_binary == application_binary
                   && sink_input.is_muted == is_muted
                   && sink_input.volume == volume;
        }

        /** Gets the name to be shown to the user:
         * "index:application_name"
         */
        public static uint32 get_hash_map_key (uint32 i) {
            return i;
        }

        construct {
            volume_operations = new LinkedList<Operation> ();
        }
    }
}
07070100000081000081A4000000000000000000000001690B16EB000009F8000000000000000000000000000000000000005100000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/volume/sinkInputRow.valanamespace SwayNotificationCenter.Widgets {
    public class SinkInputRow : Gtk.ListBoxRow {
        Gtk.Box container;
        Gtk.Image icon = new Gtk.Image ();
        Gtk.Label label = new Gtk.Label (null);
        Gtk.Scale scale = new Gtk.Scale.with_range (Gtk.Orientation.HORIZONTAL, 0, 100, 1);

        public unowned PulseSinkInput sink_input;

        private unowned PulseDaemon client;

        private bool show_per_app_icon;
        private bool show_per_app_label;

        public SinkInputRow (PulseSinkInput sink_input, PulseDaemon client,
                             int icon_size, bool show_per_app_icon, bool show_per_app_label) {
            this.client = client;
            this.show_per_app_icon = show_per_app_icon;
            this.show_per_app_label = show_per_app_label;

            set_activatable (false);

            update (sink_input);

            scale.draw_value = false;
            scale.set_hexpand (true);

            icon.set_pixel_size (icon_size);

            container = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);

            if (show_per_app_icon) {
                container.append (icon);
            }
            if (show_per_app_label) {
                container.append (label);
            }

            container.append (scale);

            set_child (container);

            scale.value_changed.connect (() => {
                client.set_sink_input_volume (sink_input, (float) scale.get_value ());
                scale.tooltip_text = ((int) scale.get_value ()).to_string ();
            });
        }

        public void update (PulseSinkInput sink_input) {
            this.sink_input = sink_input;

            if (show_per_app_icon) {
                string icon_name;
                if (sink_input.application_icon_name != null) {
                    icon_name = sink_input.application_icon_name;
                } else {
                    icon_name = sink_input.application_binary;
                }
                var theme = Gtk.IconTheme.get_for_display (Gdk.Display.get_default ());
                if (theme.has_icon (icon_name)) {
                    icon.set_from_icon_name (icon_name);
                } else {
                    icon.set_from_icon_name ("application-x-executable");
                }
            }

            if (show_per_app_label) {
                label.set_text (this.sink_input.name);
            }

            scale.set_value (sink_input.volume);
            scale.tooltip_text = ((int) scale.get_value ()).to_string ();
        }
    }
}
07070100000082000081A4000000000000000000000001690B16EB000027D9000000000000000000000000000000000000004B00000000SwayNotificationCenter-0.12.3/src/controlCenter/widgets/volume/volume.valanamespace SwayNotificationCenter.Widgets {
    public class Volume : BaseWidget {
        public override string widget_name {
            get {
                return "volume";
            }
        }

        Gtk.Box main_volume_slider_container = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
        Gtk.Label label_widget = new Gtk.Label (null);
        Gtk.Scale slider = new Gtk.Scale.with_range (Gtk.Orientation.HORIZONTAL, 0, 100, 1);

        // Per app volume control
        Gtk.ListBox levels_listbox;
        IterListBoxController list_box_controller;
        Gtk.ToggleButton reveal_button;
        Gtk.Revealer revealer;
        Gtk.Label no_sink_inputs_label;
        string empty_label = "No active sink input";

        string ?expand_label = null;
        string ?collapse_label = null;

        [Version (deprecated = true, replacement = "CSS root variable")]
        int icon_size = -1;

        Gtk.RevealerTransitionType revealer_type = Gtk.RevealerTransitionType.SLIDE_DOWN;
        int revealer_duration = 250;

        private PulseDevice ?default_sink = null;
        private PulseDaemon client = new PulseDaemon ();

        private bool show_per_app = false;
        private bool show_per_app_icon = true;
        private bool show_per_app_label = false;
        private bool expand_per_app = false;

        construct {
            this.client.change_default_device.connect (default_device_changed);

            slider.value_changed.connect (() => {
                if (default_sink != null) {
                    this.client.set_device_volume (
                        default_sink,
                        (float) slider.get_value ());
                    slider.tooltip_text = ((int) slider.get_value ()).to_string ();
                }
            });
        }

        public Volume (string suffix, SwayncDaemon swaync_daemon, NotiDaemon noti_daemon) {
            base (suffix, swaync_daemon, noti_daemon);

            Json.Object ?config = get_config (this);
            if (config != null) {
                string ?label = get_prop<string> (config, "label");
                label_widget.set_label (label ?? "Volume");

                bool show_per_app_found;
                bool ?show_per_app = get_prop<bool> (config, "show-per-app",
                                                     out show_per_app_found);
                if (show_per_app_found) {
                    this.show_per_app = show_per_app;
                }

                bool show_per_app_icon_found;
                bool ?show_per_app_icon = get_prop<bool> (config, "show-per-app-icon",
                                                          out show_per_app_icon_found);
                if (show_per_app_icon_found) {
                    this.show_per_app_icon = show_per_app_icon;
                }

                bool show_per_app_label_found;
                bool ?show_per_app_label = get_prop<bool> (config, "show-per-app-label",
                                                           out show_per_app_label_found);
                if (show_per_app_label_found) {
                    this.show_per_app_label = show_per_app_label;
                }

                bool expand_per_app_found;
                bool ?expand_per_app = get_prop<bool> (config, "expand-per-app",
                                                       out expand_per_app_found);
                if (expand_per_app_found) {
                    this.expand_per_app = expand_per_app;
                }

                string ?el = get_prop<string> (config, "empty-list-label");
                if (el != null) {
                    empty_label = el;
                }

                string ?l1 = get_prop<string> (config, "expand-button-label");
                if (l1 != null) {
                    expand_label = l1;
                }
                string ?l2 = get_prop<string> (config, "collapse-button-label");
                if (l2 != null) {
                    collapse_label = l2;
                }

                int i = int.max (get_prop<int> (config, "icon-size"), 0);
                if (i != 0) {
                    icon_size = i;
                }

                revealer_duration = int.max (0, get_prop<int> (config, "animation-duration"));
                if (revealer_duration == 0) {
                    revealer_duration = 250;
                }

                string ?animation_type = get_prop<string> (config, "animation-type");
                if (animation_type != null) {
                    switch (animation_type) {
                        default :
                        case "none" :
                            revealer_type = Gtk.RevealerTransitionType.NONE;
                            break;
                        case "slide_up":
                            revealer_type = Gtk.RevealerTransitionType.SLIDE_UP;
                            break;
                        case "slide_down":
                            revealer_type = Gtk.RevealerTransitionType.SLIDE_DOWN;
                            break;
                    }
                }
            }

            this.orientation = Gtk.Orientation.VERTICAL;

            slider.draw_value = false;
            slider.set_hexpand (true);

            main_volume_slider_container.append (label_widget);
            main_volume_slider_container.append (slider);
            append (main_volume_slider_container);

            if (show_per_app) {
                revealer = new Gtk.Revealer ();
                revealer.transition_type = revealer_type;
                revealer.transition_duration = revealer_duration;
                levels_listbox = new Gtk.ListBox ();
                levels_listbox.add_css_class ("per-app-volume");
                levels_listbox.set_activate_on_single_click (true);
                levels_listbox.set_selection_mode (Gtk.SelectionMode.NONE);
                revealer.set_child (levels_listbox);

                list_box_controller = new IterListBoxController (levels_listbox);

                if (this.client.active_sinks.size == 0) {
                    no_sink_inputs_label = new Gtk.Label (empty_label);
                    list_box_controller.append (no_sink_inputs_label);
                }

                foreach (var item in this.client.active_sinks.values) {
                    SinkInputRow row = new SinkInputRow (item, client,
                                                         icon_size, show_per_app_icon,
                                                         show_per_app_label);
                    list_box_controller.append (row);
                }

                this.client.change_active_sink.connect (active_sink_change);
                this.client.new_active_sink.connect (active_sink_added);
                this.client.remove_active_sink.connect (active_sink_removed);

                reveal_button = new Gtk.ToggleButton ();
                reveal_button.set_active (expand_per_app);
                revealer.set_reveal_child (expand_per_app);

                set_button_icon ();
                reveal_button.toggled.connect (() => {
                    set_button_icon ();
                    revealer.set_reveal_child (reveal_button.active);
                });
                main_volume_slider_container.append (reveal_button);
                append (revealer);
            }
        }

        private void set_button_icon () {
            if (!reveal_button.active) {
                if (expand_label == null) {
                    reveal_button.set_icon_name ("swaync-up-small-symbolic");
                } else {
                    reveal_button.set_label (expand_label);
                }
            } else {
                if (collapse_label == null) {
                    reveal_button.set_icon_name ("swaync-down-small-symbolic");
                } else {
                    reveal_button.set_label (collapse_label);
                }
            }
        }

        public override void on_cc_visibility_change (bool val) {
            if (val) {
                this.client.start ();
            } else {
                this.client.close ();
            }
            if (show_per_app) {
                reveal_button.set_active (expand_per_app);
            }
        }

        private void default_device_changed (PulseDevice device) {
            if (device != null && device.direction == PulseAudio.Direction.OUTPUT) {
                this.default_sink = device;
                slider.set_value (device.volume);
            }
        }

        private void active_sink_change (PulseSinkInput sink) {
            foreach (unowned Gtk.Widget row in list_box_controller.get_children ()) {
                if (!(row is SinkInputRow)) {
                    continue;
                }
                var s = (SinkInputRow) row;
                if (s.sink_input.cmp (sink)) {
                    s.update (sink);
                    break;
                }
            }
        }

        private void active_sink_added (PulseSinkInput sink) {
            // one element added -> remove the empty label
            if (this.client.active_sinks.size == 1) {
                var label = levels_listbox.get_first_child ();
                list_box_controller.remove ((Gtk.Widget) label);
            }
            SinkInputRow row = new SinkInputRow (sink, client,
                                                 icon_size, show_per_app_icon,
                                                 show_per_app_label);
            list_box_controller.append (row);
        }

        private void active_sink_removed (PulseSinkInput sink) {
            foreach (unowned Gtk.Widget row in list_box_controller.get_children ()) {
                if (!(row is SinkInputRow)) {
                    continue;
                }
                var s = (SinkInputRow) row;
                if (s.sink_input.cmp (sink)) {
                    list_box_controller.remove (row);
                    break;
                }
            }
            if (levels_listbox.get_first_child () == null) {
                list_box_controller.append (no_sink_inputs_label);
            }
        }
    }
}
07070100000083000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000003400000000SwayNotificationCenter-0.12.3/src/dismissableWidget07070100000084000081A4000000000000000000000001690B16EB000019CF000000000000000000000000000000000000004B00000000SwayNotificationCenter-0.12.3/src/dismissableWidget/dismissableWidget.valapublic enum SwipeDirection {
    SWIPE_LEFT, SWIPE_RIGHT;
}

public class DismissibleWidget : Gtk.Widget, Adw.Swipeable {
    protected unowned Gtk.Widget _child = null;
    public unowned Gtk.Widget child {
        get {
            return _child;
        }
        set {
            _child = value;
            if (_child != null) {
                _child.unparent ();
                _child.set_parent (this);
            }
        }
    }

    public Gtk.Orientation orientation {
        get;
        private set;
        default = Gtk.Orientation.HORIZONTAL;
    }

    // Animation
    Adw.SpringAnimation animation;

    // Swipe Gesture
    Adw.SwipeTracker swipe_tracker;

    bool transition_running = false;
    bool gesture_active = false;
    private double _swipe_progress = 0.0;
    public double swipe_progress {
        get {
            return _swipe_progress;
        }
        set {
            _swipe_progress = value;
            queue_allocate ();
        }
    }

    SwipeDirection swipe_direction = SwipeDirection.SWIPE_RIGHT;

    construct {
        swipe_tracker = new Adw.SwipeTracker (this);
        swipe_tracker.set_orientation (orientation);
        swipe_tracker.set_reversed (true);
        swipe_tracker.set_upper_overshoot (true);
        swipe_tracker.set_lower_overshoot (true);
        swipe_tracker.set_allow_long_swipes (true);
        swipe_tracker.set_enabled (true);
        swipe_tracker.set_allow_mouse_drag (true);

        swipe_tracker.prepare.connect (swipe_prepare_cb);
        swipe_tracker.update_swipe.connect (swipe_update_swipe_cb);
        swipe_tracker.end_swipe.connect (swipe_end_swipe_cb);

        double[] snap_dir = get_snap_points ();
        Adw.PropertyAnimationTarget target = new Adw.PropertyAnimationTarget (this,
                                                                              "swipe-progress");
        animation = new Adw.SpringAnimation (this, snap_dir[0], snap_dir[1],
                                             new Adw.SpringParams (1, 0.5, 500),
                                             target);
        animation.set_clamp (true);
        animation.done.connect (animation_done_cb);
    }

    public override void dispose () {
        if (child != null) {
            child.unparent ();
            child = null;
        }
        base.dispose ();
    }

    public signal void dismissed ();

    public void set_can_dismiss (bool state) {
        swipe_tracker.set_enabled (state);
    }

    /*
     * Overrides
     */

    protected override Gtk.SizeRequestMode get_request_mode () {
        return Gtk.SizeRequestMode.HEIGHT_FOR_WIDTH;
    }

    protected override void measure (Gtk.Orientation orientation, int for_size,
                                     out int minimum, out int natural,
                                     out int minimum_baseline, out int natural_baseline) {
        minimum = 0;
        natural = 0;
        minimum_baseline = -1;
        natural_baseline = -1;

        if (child == null || !child.should_layout ()) {
            return;
        }

        child.measure (orientation, for_size,
                       out minimum, out natural, null, null);
    }

    protected override void size_allocate (int width, int height, int baseline) {
        if (child == null || !child.should_layout ()) {
            return;
        }

        int child_width = width;
        int child_height = height;

        double x = 0;
        if (get_direction () == Gtk.TextDirection.RTL) {
            x -= (width * swipe_progress);
        } else {
            x += (width * swipe_progress);
        }

        Gsk.Transform transform = new Gsk.Transform ()
             .translate (Graphene.Point ().init ((float) x, 0));

        child.allocate (child_width, child_height, baseline, transform);
    }

    protected override void snapshot (Gtk.Snapshot snapshot) {
        snapshot.push_opacity (1 - swipe_progress.abs ());
        snapshot_child (child, snapshot);
        snapshot.pop ();
    }

    /*
     * Callbacks
     */

    private void animation_done_cb () {
        transition_running = false;
        if (swipe_progress != 0) {
            dismissed ();
        }
    }

    private void swipe_prepare_cb (Adw.NavigationDirection direction) {
        gesture_active = true;
        if (transition_running) {
            animation.pause ();
        } else {
            transition_running = true;
        }
    }

    private void swipe_update_swipe_cb (double distance) {
        swipe_progress = distance;
    }

    private void swipe_end_swipe_cb (double velocity, double to) {
        if (!gesture_active) {
            return;
        }

        animation.set_value_from (swipe_progress);
        animation.set_value_to (to);
        animation.set_initial_velocity (velocity);

        // Disable user input if dismissed
        set_can_target (to == 0);

        animation.play ();

        gesture_active = false;
    }

    /*
     * Methods
     */

    public void set_gesture_direction (SwipeDirection swipe_direction) {
        this.swipe_direction = swipe_direction;
        // Reset the position
        swipe_progress = 0.0;
    }

    /*
     * Swipe gesture
     */

    /** Gets the progress this will snap back to after the gesture is canceled. */
    public double get_cancel_progress () {
        return 0;
    }

    /** Gets the swipe distance of this. */
    public double get_distance () {
        return get_width ();
    }

    /** Gets the current progress of this. */
    public double get_progress () {
        if (!transition_running) {
            return 0;
        }
        return this.swipe_progress;
    }

    /** Gets the snap points of this. */
    public double[] get_snap_points () {
        switch (swipe_direction) {
            case SwipeDirection.SWIPE_LEFT:
                return new double[] { -1, 0 };
            default:
            case SwipeDirection.SWIPE_RIGHT:
                return new double[] { 0, 1 };
        }
    }

    /**
     * Gets the area this can start a swipe from for the given direction
     * and gesture type.
     */
    public Gdk.Rectangle get_swipe_area (Adw.NavigationDirection direction,
                                         bool is_drag) {
        Gtk.Allocation alloc = Gtk.Allocation ();
        Graphene.Rect bounds;
        this.compute_bounds (this, out bounds);
        alloc.width = (int) bounds.size.width;
        alloc.height = (int) bounds.size.height;
        alloc.x = (int) bounds.origin.x;
        alloc.y = (int) bounds.origin.y;

        return alloc;
    }
}
07070100000085000081A4000000000000000000000001690B16EB000046AE000000000000000000000000000000000000003100000000SwayNotificationCenter-0.12.3/src/functions.valanamespace SwayNotificationCenter {
    public class Functions {
        const Gsk.ScalingFilter SCALING_FILTER = Gsk.ScalingFilter.NEAREST;

        private static Gtk.CssProvider system_css_provider;
        private static Gtk.CssProvider user_css_provider;

        private Functions () {}

        public static void init () {
            system_css_provider = new Gtk.CssProvider ();
            user_css_provider = new Gtk.CssProvider ();

            // Init resources
            var theme = Gtk.IconTheme.get_for_display (Gdk.Display.get_default ());
            theme.add_resource_path ("/org/erikreider/swaync/icons");
        }

        public static string uri_to_path (owned string uri) {
            uri = uri.strip ();
            const string URI_PREFIX = "file://";
            bool is_uri = (uri.length >= URI_PREFIX.length
                           && uri.slice (0, URI_PREFIX.length) == URI_PREFIX);
            if (is_uri) {
                // Try as a URI (file:// is the only URI schema supported right now)
                uri = uri.slice (URI_PREFIX.length, uri.length);
            }
            return uri;
        }

        public static void set_image_uri (owned string uri,
                                          Gtk.Image img,
                                          bool file_exists,
                                          bool is_theme_icon = false) {
            const string URI_PREFIX = "file://";
            bool is_uri = (uri.length >= URI_PREFIX.length
                           && uri.slice (0, URI_PREFIX.length) == URI_PREFIX);
            if (!is_theme_icon && (is_uri || file_exists)) {
                // Try as a URI (file:// is the only URI schema supported right now)
                try {
                    if (is_uri) {
                        uri = uri.slice (URI_PREFIX.length, uri.length);
                    }

                    Gtk.Requisition natural_size;
                    img.get_preferred_size (null, out natural_size);

                    Gdk.Pixbuf pixbuf = new Gdk.Pixbuf.from_file_at_size (
                        Uri.unescape_string (uri),
                        natural_size.width, natural_size.height);

                    Gdk.Texture texture = Gdk.Texture.for_pixbuf (pixbuf);
                    img.set_from_paintable (texture);
                } catch (Error e) {
                    stderr.printf (e.message + "\n");
                }
            }

            // Try as icon name
            if (img.storage_type == Gtk.ImageType.EMPTY) {
                unowned Gdk.Display display = Gdk.Display.get_default ();
                unowned Gtk.IconTheme icon_theme = Gtk.IconTheme.get_for_display (display);
                if (icon_theme.has_icon (uri)) {
                    img.set_from_icon_name (uri);
                }
            }
        }

        public static void set_image_data (ImageData data,
                                           Gtk.Image img) {
            Gtk.Requisition natural_size;
            img.get_preferred_size (null, out natural_size);

            Gdk.Pixbuf pixbuf = new Gdk.Pixbuf.with_unowned_data (data.data,
                                                                  Gdk.Colorspace.RGB,
                                                                  data.has_alpha,
                                                                  data.bits_per_sample,
                                                                  data.width,
                                                                  data.height,
                                                                  data.rowstride,
                                                                  null);
            Gdk.Pixbuf scaled = pixbuf.scale_simple (natural_size.width,
                                                     natural_size.height,
                                                     Gdk.InterpType.BILINEAR);

            Gdk.Texture texture = Gdk.Texture.for_pixbuf (scaled);
            img.set_from_paintable (texture);
        }

        /** Load the package provided CSS file as a base.
         * Without this, an empty user CSS file would result in widgets
         * with default GTK style properties
         */
        public static bool load_css (string ?style_path) {
            int css_priority = ConfigModel.instance.cssPriority.get_priority ();

            // Load packaged CSS as backup
            string system_css = get_style_path (null, true);
            system_css = File.new_for_path (system_css).get_path () ?? system_css;
            if (custom_packaged_css != null) {
                system_css = custom_packaged_css;
            }
            if (!skip_packaged_css) {
                message ("Loading CSS: \"%s\"", system_css);
                system_css_provider.load_from_path (system_css);
                Gtk.StyleContext.add_provider_for_display (
                    Gdk.Display.get_default (),
                    system_css_provider,
                    css_priority);
            } else {
                message ("Skipping system CSS: \"%s\"", system_css);
            }

            // Load user CSS
            string user_css = get_style_path (style_path);
            user_css = File.new_for_path (user_css).get_path () ?? user_css;
            message ("Loading CSS: \"%s\"", user_css);
            user_css_provider.load_from_path (user_css);
            Gtk.StyleContext.add_provider_for_display (
                Gdk.Display.get_default (),
                user_css_provider,
                css_priority);

            return true;
        }

        public static string get_style_path (owned string ?custom_path,
                                             bool only_system = false) {
            string[] paths = {};
            if (custom_path != null && custom_path.length > 0) {
                // Replaces the home directory relative path with a absolute path
                if (custom_path.get (0) == '~') {
                    custom_path = Environment.get_home_dir () + custom_path[1 :];
                }
                paths += custom_path;
            }
            if (!only_system) {
                paths += Path.build_path (Path.DIR_SEPARATOR.to_string (),
                                          Environment.get_user_config_dir (),
                                          "swaync/style.css");
            }

            foreach (var path in Environment.get_system_config_dirs ()) {
                paths += Path.build_path (Path.DIR_SEPARATOR.to_string (),
                                          path, "swaync/style.css");
            }
            // Fallback location. Specified in postinstall.py. Mostly for Debian
            paths += "/usr/local/etc/xdg/swaync/style.css";

            info ("Looking for CSS file in these directories:\n\t- %s",
                  string.joinv ("\n\t- ", paths));

            string path = "";
            foreach (string try_path in paths) {
                if (File.new_for_path (try_path).query_exists ()) {
                    path = try_path;
                    break;
                }
            }
            if (path == "") {
                stderr.printf (
                    "COULD NOT FIND CSS FILE! REINSTALL THE PACKAGE!\n");
                Process.exit (1);
            }
            return path;
        }

        public static string get_config_path (owned string ?custom_path) {
            string[] paths = {};
            if (custom_path != null && (custom_path = custom_path.strip ()).length > 0) {
                // Replaces the home directory relative path with a absolute path
                if (custom_path.get (0) == '~') {
                    custom_path = Environment.get_home_dir () + custom_path[1 :];
                }

                if (File.new_for_path (custom_path).query_exists ()) {
                    paths += custom_path;
                } else {
                    warning ("Custom config file \"%s\" not found, skipping...", custom_path);
                }
            }
            paths += Path.build_path (Path.DIR_SEPARATOR.to_string (),
                                      Environment.get_user_config_dir (),
                                      "swaync/config.json");
            foreach (var path in Environment.get_system_config_dirs ()) {
                paths += Path.build_path (Path.DIR_SEPARATOR.to_string (),
                                          path, "swaync/config.json");
            }
            // Fallback location. Specified in postinstall.py. Mostly for Debian
            paths += "/usr/local/etc/xdg/swaync/config.json";

            info ("Looking for config file in these directories:\n\t- %s",
                  string.joinv ("\n\t- ", paths));

            string path = "";
            foreach (string try_path in paths) {
                if (File.new_for_path (try_path).query_exists ()) {
                    path = try_path;
                    break;
                }
            }
            if (path == "") {
                stderr.printf (
                    "COULD NOT FIND CONFIG FILE! REINSTALL THE PACKAGE!\n");
                Process.exit (1);
            }
            return path;
        }

        /** Gets the base type of a type if it's derivited */
        public static Type get_base_type (Type type) {
            if (type.is_derived ()) {
                while (type.is_derived ()) {
                    type = type.parent ();
                }
            }
            return type;
        }

        /** Scales the Texture to fit the given dimensions */
        public static void scale_texture (Gdk.Texture texture,
                                          int buffer_width,
                                          int buffer_height,
                                          int img_scale,
                                          Gtk.Snapshot snapshot) {
            int width = texture.width / img_scale;
            int height = texture.height / img_scale;
            double window_ratio = (double) buffer_width / buffer_height;
            double bg_ratio = width / height;
            snapshot.save ();
            if (window_ratio > bg_ratio) { // Taller wallpaper than monitor
                double scale = (double) buffer_width / width;
                if (scale * height < buffer_height) {
                    draw_scale_wide (buffer_width, width, buffer_height, height, snapshot, texture);
                } else {
                    draw_scale_tall (buffer_width, width, buffer_height, height, snapshot, texture);
                }
            } else { // Wider wallpaper than monitor
                double scale = (double) buffer_height / height;
                if (scale * width < buffer_width) {
                    draw_scale_tall (buffer_width, width, buffer_height, height, snapshot, texture);
                } else {
                    draw_scale_wide (buffer_width, width, buffer_height, height, snapshot, texture);
                }
            }

            snapshot.restore ();
        }

        private static void draw_scale_tall (int buffer_width,
                                             int width,
                                             int buffer_height,
                                             int height,
                                             Gtk.Snapshot snapshot,
                                             Gdk.Texture texture) {
            float scale = (float) buffer_width / width;
            snapshot.scale (scale, scale);
            float x = 0;
            float y = (float) (buffer_height / 2 / scale - height / 2);
            snapshot.append_scaled_texture (texture,
                                            SCALING_FILTER,
                                            { { x, y }, { width, height } });
        }

        private static void draw_scale_wide (int buffer_width,
                                             int width,
                                             int buffer_height,
                                             int height,
                                             Gtk.Snapshot snapshot,
                                             Gdk.Texture texture) {
            float scale = (float) buffer_height / height;
            snapshot.scale (scale, scale);
            float x = (float) (buffer_width / 2 / scale - width / 2);
            float y = 0;
            snapshot.append_scaled_texture (texture,
                                            SCALING_FILTER,
                                            { { x, y }, { width, height } });
        }

        public delegate bool FilterFunc (char character);

        public static string filter_string (string body, FilterFunc func) {
            string result = "";
            foreach (char char in (char[]) body.data) {
                if (!func (char)) {
                    continue;
                }
                result += char.to_string ();
            }
            return result;
        }

        public static async bool execute_command (string cmd, string[] env_additions = {},
                                                  out string msg) {
            msg = "";
            try {
                string[] spawn_env = Environ.get ();
                // Export env variables
                foreach (string additions in env_additions) {
                    spawn_env += additions;
                }

                string[] argvp;
                Shell.parse_argv ("/bin/sh -c \"%s\"".printf (cmd), out argvp);

                if (argvp[0].has_prefix ("~")) {
                    argvp[0] = Environment.get_home_dir () + argvp[0].substring (1);
                }

                Pid child_pid;
                int std_output;
                Process.spawn_async_with_pipes (
                    "/",
                    argvp,
                    spawn_env,
                    SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
                    null,
                    out child_pid,
                    null,
                    out std_output,
                    null);

                // stdout:
                string res = "";
                IOChannel output = new IOChannel.unix_new (std_output);
                output.add_watch (IOCondition.IN | IOCondition.HUP, (channel, condition) => {
                    if (condition == IOCondition.HUP) {
                        return false;
                    }
                    try {
                        if (channel.read_line (out res, null, null) == IOStatus.NORMAL) {
                            debug ("Exec output:\n%s", res);
                        } else {
                            res = "";
                        }
                        return true;
                    } catch (IOChannelError e) {
                        warning ("stdout: IOChannelError: %s", e.message);
                        return false;
                    } catch (ConvertError e) {
                        warning ("stdout: ConvertError: %s", e.message);
                        return false;
                    }
                });

                // Close the child when the spawned process is idling
                int end_status = 0;
                ChildWatch.add (child_pid, (pid, status) => {
                    Process.close_pid (pid);
                    GLib.FileUtils.close (std_output);
                    end_status = status;
                    execute_command.callback ();
                });
                // Waits until `execute_command.callback()` is called above
                yield;
                msg = res;
                return end_status == 0;
            } catch (Error e) {
                warning ("Execute Command Error: %s", e.message);
                msg = e.message;
                return false;
            }
        }

        public static unowned Wl.Display get_wl_display () {
            unowned var display = Gdk.Display.get_default ();
            if (display is Gdk.Wayland.Display) {
                return ((Gdk.Wayland.Display) display).get_wl_display ();
            }
            error ("Only supports Wayland!");
        }

        public static Wl.Surface *get_wl_surface (Gdk.Surface surface) {
            if (surface is Gdk.Wayland.Surface) {
                return ((Gdk.Wayland.Surface) surface).get_wl_surface ();
            }
            error ("Only supports Wayland!");
        }

        public static double lerp (double a, double b, double t) {
            return a * (1.0 - t) + b * t;
        }

        public static unowned Gdk.Monitor ?try_get_monitor (string name) {
            if (name == null || name.length == 0) {
                return null;
            }

            for (int i = 0; i < monitors.get_n_items (); i++) {
                Object ?obj = monitors.get_item (i);
                if (obj == null || !(obj is Gdk.Monitor)) {
                    continue;
                }
                unowned Gdk.Monitor monitor = (Gdk.Monitor) obj;

                if (monitor.connector == name) {
                    return monitor;
                }

                // Try matching a string consisting of the manufacturer + model + serial number.
                // Just like Sway does (sway-output(5) man page)
                string id = "%s %s %s".printf (monitor.manufacturer,
                                               monitor.model,
                                               monitor.description);
                if (id == name) {
                    return monitor;
                }
            }

            return null;
        }

        public static string ?monitor_to_string (Gdk.Monitor ?monitor) {
            if (monitor == null) {
                return null;
            }
            return "%s, %ix%i, x:%i y:%i, scale:%f\n".printf (
                monitor.get_description (),
                monitor.geometry.width, monitor.geometry.height,
                monitor.geometry.x, monitor.geometry.y,
                monitor.scale);
        }
    }
}
07070100000086000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002E00000000SwayNotificationCenter-0.12.3/src/iterHelpers07070100000087000081A4000000000000000000000001690B16EB0000049D000000000000000000000000000000000000003B00000000SwayNotificationCenter-0.12.3/src/iterHelpers/iterBox.valapublic class IterBox : Gtk.Box {
    public uint length { get; private set; default = 0; }

    private List<Gtk.Widget> children = new List<Gtk.Widget> ();

    public IterBox (Gtk.Orientation orientation, int spacing) {
        Object (orientation: orientation, spacing: spacing);
        set_name ("iterbox");
    }

    public override void dispose () {
        foreach (Gtk.Widget child in children) {
            remove (child);
        }

        base.dispose ();
    }

    private void on_add (Gtk.Widget child) {
        length++;
        child.destroy.connect (() => {
            children.remove (child);
        });
    }

    public List<weak Gtk.Widget> get_children () {
        return children.copy ();
    }

    public new void append (Gtk.Widget child) {
        children.append (child);
        base.append (children.last ().data);
        on_add (child);
    }

    public new void prepend (Gtk.Widget child) {
        children.prepend (child);
        base.prepend (children.first ().data);
        on_add (child);
    }

    public new void remove (Gtk.Widget child) {
        children.remove (child);
        base.remove (child);
        length--;
    }
}
07070100000088000081A4000000000000000000000001690B16EB000004D1000000000000000000000000000000000000004900000000SwayNotificationCenter-0.12.3/src/iterHelpers/iterListBoxController.valapublic class IterListBoxController : Object {
    public int length { get; private set; default = 0; }

    private List<Gtk.Widget> children = new List<Gtk.Widget> ();
    public unowned Gtk.ListBox list_box {
        get;
        private set;
    }

    public IterListBoxController (Gtk.ListBox list_box) {
        this.list_box = list_box;
    }

    private void on_add (Gtk.Widget child) {
        length++;
        child.destroy.connect (this.remove);
    }

    // NOTE: Not sorted
    public List<weak Gtk.Widget> get_children () {
        return children.copy ();
    }

    public void append (Gtk.Widget child) {
        children.append (child);
        list_box.append (children.last ().data);
        on_add (child);
    }

    public void prepend (Gtk.Widget child) {
        children.prepend (child);
        list_box.prepend (children.first ().data);
        on_add (child);
    }

    public void insert (Gtk.Widget child, int position) {
        children.insert (child, position);
        list_box.insert (children.nth (position).data, position);
        on_add (child);
    }

    public void remove (Gtk.Widget child) {
        children.remove (child);
        list_box.remove (child);
        length--;
    }
}
07070100000089000081A4000000000000000000000001690B16EB000027E0000000000000000000000000000000000000002C00000000SwayNotificationCenter-0.12.3/src/main.valanamespace SwayNotificationCenter {
    static SwayncDaemon swaync_daemon;
    static unowned ListModel ?monitors = null;
    static Swaync app;
    static Settings self_settings;

    static HashTable<string, unowned Gdk.PixbufFormat> pixbuf_mime_types;

    // Args
    static string ?style_path;
    static string ?config_path;
    // Dev args
    static bool skip_packaged_css = false;
    static string ?custom_packaged_css;

    private struct EnvironmentVariable {
        string name;

        public EnvironmentVariable (string name) {
            this.name = name;
        }

        public string to_string (string[] envp) {
            return "%s=%s".printf (name, Environ.get_variable (envp, name) ?? "");
        }
    }

    public class Swaync : Gtk.Application {
        static bool activated = false;

        public signal void config_reload (ConfigModel ?old_config, ConfigModel new_config);

        public Swaync () {
            Object (
                application_id : "org.erikreider.swaync",
                flags: ApplicationFlags.DEFAULT_FLAGS
            );

            try {
                register ();
                if (get_is_remote ()) {
                    printerr ("An instance of SwayNotificationCenter is already running!\n");
                    Process.exit (1);
                }
            } catch (Error e) {
                error (e.message);
            }
        }

        public override void activate () {
            if (activated) {
                return;
            }
            activated = true;
            Functions.load_css (style_path);

            pixbuf_mime_types =
                new HashTable<string, unowned Gdk.PixbufFormat> (str_hash, str_equal);
            SList<weak Gdk.PixbufFormat> formats = Gdk.Pixbuf.get_formats ();
            foreach (weak Gdk.PixbufFormat format in formats) {
                foreach (string mime_type in format.get_mime_types ()) {
                    pixbuf_mime_types.set (mime_type, format);
                }
            }

            hold ();

            unowned Gdk.Display ?display = Gdk.Display.get_default ();
            if (display == null) {
                error ("Could not get Display!");
            }
            monitors = display.get_monitors ();
            assert_nonnull (monitors);

            print_monitors ();

            swaync_daemon = new SwayncDaemon ();
            Bus.own_name (BusType.SESSION, "org.erikreider.swaync.cc",
                          BusNameOwnerFlags.NONE,
                          on_cc_bus_aquired,
                          () => {},
                          () => {
                stderr.printf (
                    "Could not acquire swaync name!...\n");
                Process.exit (1);
            });

            add_window (swaync_daemon.noti_daemon.control_center);
        }

        private void print_monitors () {
            uint num_monitors = monitors.get_n_items ();
            string monitors_string = "";
            for (uint i = 0; i < num_monitors; i++) {
                Gdk.Monitor ?mon = (Gdk.Monitor) monitors.get_item (i);
                if (mon == null) {
                    continue;
                }
                monitors_string += Functions.monitor_to_string (mon);
            }
            info ("Monitors:\n%s", monitors_string);
        }

        void on_cc_bus_aquired (DBusConnection conn) {
            try {
                conn.register_object ("/org/erikreider/swaync/cc", swaync_daemon);
            } catch (IOError e) {
                stderr.printf ("Could not register CC service\n");
                Process.exit (1);
            }
        }

        public static int main (string[] args) {
            if (args.length > 0) {
                for (uint i = 1; i < args.length; i++) {
                    string arg = args[i];
                    switch (arg) {
                        case "-s" :
                        case "--style" :
                            style_path = args[++i];
                            break;
                        case "--skip-system-css":
                            skip_packaged_css = true;
                            break;
                        case "--custom-system-css":
                            custom_packaged_css = args[++i];
                            break;
                        case "-c":
                        case "--config":
                            config_path = args[++i];
                            break;
                        case "-v":
                        case "--version":
                            stdout.printf ("%s\n", Constants.VERSION);
                            return 0;
                        case "-h":
                        case "--help":
                            print_help (args);
                            return 0;
                        default:
                            print_help (args);
                            return 1;
                    }
                }
            }

            print_startup_info ();

            // Register custom Widgets so that they can be used in .ui template files
            typeof (AnimatedList).ensure ();
            typeof (AnimatedListItem).ensure ();
            typeof (Underlay).ensure ();

            ConfigModel.init (config_path);

            // Fixes custom themes messing with the default/custom CSS styling
            if (ConfigModel.instance.ignore_gtk_theme) {
                Environment.unset_variable ("GTK_THEME");
            }

            Gtk.init ();
            Adw.init ();

            Functions.init ();
            self_settings = new Settings ("org.erikreider.swaync");

            app = new Swaync ();
            return app.run ();
        }

        private static void print_startup_info () {
            print ("Starting SwayNotificationCenter version %s\n", Constants.VERSION);

            // Log distro information
            string info_paths[5] = {
                "/etc/lsb-release",
                "/etc/os-release",
                "/etc/debian_version",
                "/etc/redhat-release",
                "/etc/gentoo-release",
            };
            foreach (unowned string path in info_paths) {
                File ?file = File.new_for_path (path);
                if (file == null) {
                    continue;
                }
                string lines = "";
                try {
                    FileInputStream file_stream = file.read (null);
                    DataInputStream data_stream = new DataInputStream (file_stream);
                    string ?line = null;
                    while ((line = data_stream.read_line (null, null)) != null) {
                        lines += "%s\n".printf (line);
                    }
                } catch (Error e) {
                    continue;
                }
                info ("Contents of %s:\n%s", path, lines);
                break;
            }

            // Log important environment variables
            EnvironmentVariable[] variables = {
                EnvironmentVariable ("XDG_CURRENT_DESKTOP"),
                EnvironmentVariable ("XDG_SESSION_DESKTOP"),
                EnvironmentVariable ("DESKTOP_SESSION"),
                EnvironmentVariable ("XDG_SESSION_TYPE"),
                EnvironmentVariable ("XDG_BACKEND"),
                EnvironmentVariable ("XDG_DATA_HOME"),
                EnvironmentVariable ("XDG_DATA_DIRS"),
                EnvironmentVariable ("SHELL"),
                // From: https://docs.gtk.org/gtk4/running.html
                EnvironmentVariable ("GTK_DEBUG"),
                EnvironmentVariable ("GTK_PATH"),
                EnvironmentVariable ("GTK_IM_MODULE"),
                EnvironmentVariable ("GTK_MEDIA"),
                EnvironmentVariable ("GTK_EXE_PREFIX"),
                EnvironmentVariable ("GTK_DATA_PREFIX"),
                EnvironmentVariable ("GTK_THEME"),
                EnvironmentVariable ("GDK_PIXBUF_MODULE_FILE"),
                EnvironmentVariable ("GDK_DEBUG"),
                EnvironmentVariable ("GSK_DEBUG"),
                EnvironmentVariable ("GDK_BACKEND"),
                EnvironmentVariable ("GDK_DISABLE"),
                EnvironmentVariable ("GDK_GL_DISABLE"),
                EnvironmentVariable ("GDK_VULKAN_DISABLE"),
                EnvironmentVariable ("GDK_WAYLAND_DISABLE"),
                EnvironmentVariable ("GSK_RENDERER"),
                EnvironmentVariable ("GSK_GPU_DISABLE"),
                EnvironmentVariable ("GSK_CACHE_TIMEOUT"),
                EnvironmentVariable ("GTK_CSD"),
                EnvironmentVariable ("GTK_A11Y"),
                EnvironmentVariable ("DESKTOP_STARTUP_ID"),
            };
            string[] envp = Environ.get ();
            string env_variables = "";
            foreach (unowned EnvironmentVariable variable in variables) {
                env_variables += "%s\n".printf (variable.to_string (envp));
            }
            info ("important environment variables:\n%s", env_variables);

            info ("Gtk4LayerShell version: %u.%u.%u",
                  GtkLayerShell.get_major_version (),
                  GtkLayerShell.get_minor_version (),
                  GtkLayerShell.get_micro_version ());
            info ("LayerShell version supported by compositor: %u",
                  GtkLayerShell.get_protocol_version ());
        }

        private static void print_help (string[] args) {
            print ("Usage:\n");
            print ("\t %s <OPTION>\n".printf (args[0]));
            print ("Help:\n");
            print ("\t -h, --help \t\t Show help options\n");
            print ("\t -v, --version \t\t Prints version\n");
            print ("Options:\n");
            print ("\t -s, --style \t\t Use a custom Stylesheet file\n");
            print ("\t -c, --config \t\t Use a custom config file\n");
            print ("\t --skip-system-css \t Skip trying to parse the packaged Stylesheet file."
                   + " Useful for CSS debugging\n");
            print ("\t --custom-system-css \t Pick a custom CSS file to use as the \"system\" CSS."
                   + " Useful for CSS debugging\n");
        }
    }
}
0707010000008A000081A4000000000000000000000001690B16EB00001212000000000000000000000000000000000000002E00000000SwayNotificationCenter-0.12.3/src/meson.build# Sets the version to a vala variable in Constants.vala
version = '@0@'.format(meson.project_version())
git = find_program('git', native: true, required: false)
if git.found()
  git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false)
  git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false)
  if git_commit.returncode() == 0 and git_branch.returncode() == 0
    version = '@0@ (git-@1@, branch \'@2@\')'.format(
      meson.project_version(),
      git_commit.stdout().strip(),
      git_branch.stdout().strip(),
    )
  endif
endif
version = 'swaync @0@'.format(version)
const_config_data = configuration_data()
const_config_data.set_quoted('VERSION', version)
const_config_data.set_quoted('VERSION_NUM', meson.project_version())
constants = configure_file(
  input : 'constants.vala.in',
  output : 'constants.vala',
  configuration : const_config_data
)

widget_sources = [
  # Helpers
  'controlCenter/widgets/baseWidget.vala',
  'controlCenter/widgets/factory.vala',
  'controlCenter/widgets/shared/toggleButton.vala',
  # Widget: Notifications
  'controlCenter/widgets/notifications/notifications.vala',
  # Widget: Title
  'controlCenter/widgets/title/title.vala',
  # Widget: Dnd
  'controlCenter/widgets/dnd/dnd.vala',
  # Widget: Label
  'controlCenter/widgets/label/label.vala',
  # Widget: MPRIS
  'controlCenter/widgets/mpris/mpris.vala',
  'controlCenter/widgets/mpris/interfaces.vala',
  'controlCenter/widgets/mpris/mpris_player.vala',
  # Widget: Menubar
  'controlCenter/widgets/menubar/menubar.vala',
  # Widget: Buttons Grid
  'controlCenter/widgets/buttonsGrid/buttonsGrid.vala',
  # Widget: Slider
  'controlCenter/widgets/slider/slider.vala',
  # Widget: Backlight Slider
  'controlCenter/widgets/backlight/backlight.vala',
  'controlCenter/widgets/backlight/backlightUtil.vala',
  # Widget: Inhibitors
  'controlCenter/widgets/inhibitors/inhibitors.vala',
]

app_sources = [
  'main.vala',
  'animatedList/animatedList.vala',
  'animatedList/animatedListItem.vala',
  'orderedHashTable/orderedHashTable.vala',
  'iterHelpers/iterBox.vala',
  'iterHelpers/iterListBoxController.vala',
  'underlay/underlay.vala',
  'configModel/configModel.vala',
  'swayncDaemon/swayncDaemon.vala',
  'notiDaemon/notiDaemon.vala',
  'notiModel/notiModel.vala',
  'notificationWindow/notificationWindow.vala',
  'dismissableWidget/dismissableWidget.vala',
  'notification/notification.vala',
  'notificationGroup/notificationGroup.vala',
  'controlCenter/controlCenter.vala',
  widget_sources,
  'blankWindow/blankWindow.vala',
  'functions.vala',
  'xdg_activation.vala',
  constants,
]

assert(vala.version() >= '0.56')

app_deps = [
  dependency('gtk4-layer-shell-0', version: '>= 1.0.4'),
  dependency('gio-2.0', version: '>= 2.50'),
  dependency('gio-unix-2.0', version: '>= 2.50'),
  dependency('gtk4', version: '>= 4.16.13'),
  dependency('gtk4-wayland', version: '>= 4.16.5'),
  dependency('json-glib-1.0', version: '>= 1.0'),
  dependency('libadwaita-1', version: '>= 1.6.1'),
  dependency('granite-7', version: '>= 7.5.0'),
  cc.find_library('m', required : true),
  vala.find_library('posix'),
  dependency('gee-0.8', version: '>=0.20.6'),
  dependency('wayland-client', version: '>=1.23.0'),
  protocol_dep,
]

# Checks if the user wants scripting enabled
if get_option('scripting')
  add_project_arguments('-D', 'WANT_SCRIPTING', language: 'vala')
endif

# Checks if the user wants to compile with the PulseAudio dependency
if get_option('pulse-audio')
  add_project_arguments('-D', 'HAVE_PULSE_AUDIO', language: 'vala')
  app_deps += [
    dependency('libpulse'),
    dependency('libpulse-mainloop-glib'),
  ]
  app_sources += [
    # Widget: Volume
    'controlCenter/widgets/volume/volume.vala',
    'controlCenter/widgets/volume/pulseDaemon.vala',
    'controlCenter/widgets/volume/pulseDevice.vala',
    'controlCenter/widgets/volume/pulseSinkInput.vala',
    'controlCenter/widgets/volume/sinkInputRow.vala',
  ]
endif

args = [
  '--target-glib=2.82',
]

executable('swaync',
  [ app_sources, app_resources ],
  vala_args: args,
  dependencies: app_deps,
  install: true,
)

executable('swaync-client',
  ['client.vala', constants],
  vala_args: args,
  dependencies: app_deps,
  install: true,
)

config_data = configuration_data()
config_data.set_quoted('JSONPATH', join_paths('/', config_path, 'configSchema.json'))
config_json = configure_file(
  input : 'config.json.in',
  output : 'config.json',
  configuration : config_data
)

install_data(config_json, install_dir : config_path)
install_data('configSchema.json', install_dir : config_path)
0707010000008B000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002D00000000SwayNotificationCenter-0.12.3/src/notiDaemon0707010000008C000081A4000000000000000000000001690B16EB00004497000000000000000000000000000000000000003D00000000SwayNotificationCenter-0.12.3/src/notiDaemon/notiDaemon.valanamespace SwayNotificationCenter {
    [DBus (name = "org.freedesktop.Notifications")]
    public class NotiDaemon : Object {
        private uint32 noti_id = 0;

        public bool dnd { get; set; default = false; }

        private HashTable<string, uint32> synchronous_ids =
            new HashTable<string, uint32> (str_hash, str_equal);

        public ControlCenter control_center;

        public unowned SwayncDaemon swaync_daemon;

        public NotiDaemon (SwayncDaemon swaync_daemon) {
            this.swaync_daemon = swaync_daemon;

            this.notify["dnd"].connect (() => on_dnd_toggle (dnd));

            on_dnd_toggle.connect ((dnd) => {
                if (!dnd || NotificationWindow.is_null) {
                    return;
                }
                NotificationWindow.instance.close_all_notifications ((noti) => {
                    return noti.param.urgency != UrgencyLevels.CRITICAL;
                });
            });

            // Init dnd from gsettings
            self_settings.bind ("dnd-state", this, "dnd", SettingsBindFlags.DEFAULT);

            this.control_center = new ControlCenter (swaync_daemon, this);
        }

        /**
         * Changes the popup-notification window visibility.
         * Closes all notifications and hides window if `value` is false
         */
        public void set_noti_window_visibility (bool value)
        throws DBusError, IOError {
            NotificationWindow.instance.change_visibility (value);
        }

        /** Toggles the current Do Not Disturb state */
        public bool toggle_dnd () throws DBusError, IOError {
            return dnd = !dnd;
        }

        /** Sets the current Do Not Disturb state */
        [DBus (name = "SetDnd")]
        public void set_do_not_disturb (bool state) throws DBusError, IOError {
            dnd = state;
        }

        /** Gets the current Do Not Disturb state */
        [DBus (name = "GetDnd")]
        public bool get_do_not_disturb () throws DBusError, IOError {
            return dnd;
        }

        /** Called when Do Not Disturb state changes */
        public signal void on_dnd_toggle (bool dnd);

        /** Method to close notification and send DISMISSED signal */
        public void manually_close_notification (uint32 id, bool timeout)
        throws DBusError, IOError {
            NotificationWindow.instance.close_notification (id, true);
            if (!timeout) {
                control_center.close_notification (id, true);
                NotificationClosed (id, ClosedReasons.DISMISSED);

                swaync_daemon.subscribe_v2 (control_center.notification_count (),
                                            dnd,
                                            control_center.get_visibility (),
                                            swaync_daemon.inhibited);
            }
        }

        /** Closes all popup and controlcenter notifications */
        public void close_all_notifications () throws DBusError, IOError {
            NotificationWindow.instance.close_all_notifications ();
            control_center.close_all_notifications ();
        }

        /** Closes latest popup notification */
        public void hide_latest_notification (bool close)
        throws DBusError, IOError {
            uint32 ?id = NotificationWindow.instance.get_latest_notification ();
            if (id == null) {
                return;
            }
            manually_close_notification (id, !close);
        }

        /** Closes all popup notifications */
        public void hide_all_notifications ()
        throws DBusError, IOError {
            NotificationWindow.instance.close_all_notifications ();
        }

        /** Activates the `action_index` action of the latest notification */
        public void latest_invoke_action (uint32 action_index)
        throws DBusError, IOError {
            NotificationWindow.instance.latest_notification_action (action_index);
        }

        /*
         * D-Bus Specification
         * https://specifications.freedesktop.org/notification-spec/latest/ar01s09.html
         */

        /**
         * It returns an array of strings. Each string describes an optional
         * capability implemented by the server.
         *
         * New vendor-specific caps may be specified as long as they start with
         * "x-vendor". For instance, "x-gnome-foo-cap". Capability names must
         * not contain spaces. They are limited to alphanumeric characters
         * and dashes ("-").
         */
        [DBus (name = "GetCapabilities")]
        public string[] get_capabilities () throws DBusError, IOError {
            return {
                       "actions",
                       "body",
                       "body-markup",
                       "body-images",
                       "persistence",
                       "synchronous",
                       "private-synchronous",
                       "x-canonical-private-synchronous",
                       "inline-reply",
            };
        }

        /**
         * Sends a notification to the notification server.
         *
         * If replaces_id is 0, the return value is a UINT32 that represent
         * the notification. It is unique, and will not be reused unless a
         * MAXINT number of notifications have been generated. An acceptable
         * implementation may just use an incrementing counter for the ID.
         * The returned ID is always greater than zero. Servers must make
         * sure not to return zero as an ID.
         *
         * If replaces_id is not 0, the returned value is the same value
         * as replaces_id.
         */
        [DBus (name = "Notify")]
        public uint32 new_notification (string app_name,
                                        uint32 replaces_id,
                                        string app_icon,
                                        string summary,
                                        string body,
                                        string[] actions,
                                        HashTable<string, Variant> hints,
                                        int expire_timeout) throws DBusError, IOError {
            uint32 id = replaces_id;
            if (replaces_id == 0 || replaces_id > noti_id) {
                id = ++noti_id;
            }

            var param = new NotifyParams (
                id,
                app_name,
                replaces_id,
                app_icon,
                summary,
                body,
                actions,
                hints,
                expire_timeout);

            // The notification visibility state
            NotificationStatusEnum state = NotificationStatusEnum.ENABLED;
            unowned OrderedHashTable<NotificationVisibility> visibilities =
                ConfigModel.instance.notification_visibility;
            foreach (string key in visibilities.get_keys ()) {
                unowned NotificationVisibility vis = visibilities[key];
                if (!vis.matches_notification (param)) {
                    continue;
                }
                state = vis.state;
                if (vis.override_urgency != UNSET) {
                    debug ("override urgency to %s\n", vis.override_urgency.to_string ());
                    param.urgency = UrgencyLevels.from_value (vis.override_urgency.to_byte ());
                }
                break;
            }

            debug ("Notification (ID:%u, state: %s): %s\n",
                   param.applied_id, state.to_string (), param.to_string ());

            // Get the notification id to replace
            uint32 replace_notification = 0;
            if (id == replaces_id) {
                replace_notification = id;
            } else if (param.synchronous != null
                       && param.synchronous.length > 0) {
                // Tries replacing without replaces_id instead
                uint32 r_id;
                // if there is any notification to replace
                if (synchronous_ids.lookup_extended (
                        param.synchronous, null, out r_id)) {
                    replace_notification = r_id;
                }
                synchronous_ids.set (param.synchronous, id);
            }

            string ?hide_notification_reason = null;
            bool show_notification = state == NotificationStatusEnum.ENABLED
                || state == NotificationStatusEnum.TRANSIENT;
            if (!show_notification) {
                hide_notification_reason =
                    "Notification status is not Enabled or Transient in Config";
            }

            // Don't show the notification window if the control center is open
            if (control_center.get_visibility ()) {
                hide_notification_reason = "Control Center is visible";
                show_notification = false;
            }

            bool bypass_dnd = param.urgency == UrgencyLevels.CRITICAL || param.swaync_bypass_dnd;
            // Don't show the notification window if dnd or inhibited
            if (!bypass_dnd && (dnd || swaync_daemon.inhibited)) {
                hide_notification_reason = "Do Not Disturb is enabled or an Inhibitor is running";
                show_notification = false;
            }

            if (show_notification) {
                if (replace_notification > 0) {
                    debug ("Replacing Notification: ID:%u\n", param.applied_id);
                    NotificationWindow.instance.replace_notification (replace_notification, param);
                } else {
                    NotificationWindow.instance.add_notification (param);
                }
            } else if (replace_notification > 0) {
                // Remove the old notification due to it not being replaced
                NotificationWindow.instance.close_notification (replace_notification, false);
            } else {
                debug ("Not displaying Notification: ID:%u, Reason: \"%s\"\n",
                       param.applied_id, hide_notification_reason);
            }

            // Only add notification to CC if it isn't IGNORED and not transient/TRANSIENT
            if (state != NotificationStatusEnum.IGNORED
                && state != NotificationStatusEnum.TRANSIENT
                && !param.transient) {
                if (replace_notification > 0) {
                    debug ("Replacing CC Notification: ID:%u\n", param.applied_id);
                    control_center.replace_notification (replace_notification, param);
                } else {
                    control_center.add_notification (param);
                }
            } else if (replace_notification > 0) {
                // Remove the old notification due to it not being replaced
                control_center.close_notification (replace_notification, false);
            } else {
                debug ("Not Placing Notification in CC: ID:%u\n", param.applied_id);
            }

#if WANT_SCRIPTING
            if (param.swaync_no_script) {
                debug ("Skipped scripts for this notification\n");
                return id;
            }
            // Run the first script if notification meets requirements
            OrderedHashTable<Script> scripts = ConfigModel.instance.scripts;
            if (scripts.length == 0) {
                return id;
            }
            this.run_scripts (param, ScriptRunOnType.RECEIVE);
#endif
            debug ("\n");
            return id;
        }

        /**
         * Runs scripts that meet the requirements of the given `param`.
         */
        [DBus (visible = false)]
        public void run_scripts (NotifyParams param, ScriptRunOnType run_on) {
#if WANT_SCRIPTING
            if (param.swaync_no_script) {
                debug ("Skipped action scripts for this notification\n");
                return;
            }
            // Run the first script if notification meets requirements
            OrderedHashTable<Script> scripts = ConfigModel.instance.scripts;
            if (scripts.length == 0) {
                return;
            }
            foreach (string key in scripts.get_keys ()) {
                unowned Script script = scripts[key];
                if (!script.matches_notification (param)) {
                    continue;
                }
                if (script.run_on != run_on) {
                    continue;
                }

                script.run_script.begin (param, (obj, res) => {
                    // Gets the end status
                    string error_msg;
                    if (script.run_script.end (res, out error_msg)) {
                        return;
                    }

                    if (!ConfigModel.instance.script_fail_notify) {
                        stderr.printf (
                            "Failed to run script: \"%s\" with exec: \"%s\"\n",
                            key, script.exec);
                    } else {
                        // Send notification with error message
                        try {
                            var _hints = new HashTable<string, Variant> (
                                str_hash,
                                str_equal);
                            // Disable scripts for this notification
                            _hints.insert ("SWAYNC_NO_SCRIPT", true);
                            _hints.insert ("urgency",
                                           UrgencyLevels.CRITICAL.to_byte ());

                            string _summary = "Failed to run script: %s".printf (key);
                            string _body = "<b>Output:</b> " + error_msg;
                            this.new_notification ("SwayNotificationCenter",
                                                   0,
                                                   "dialog-error",
                                                   _summary,
                                                   _body,
                                                   {},
                                                   _hints,
                                                   -1);
                        } catch (Error e) {
                            stderr.printf ("NOTIFING SCRIPT-FAIL ERROR: %s\n",
                                           e.message);
                        }
                    }
                });
                break;
            }
#endif
        }

        /**
         * Causes a notification to be forcefully closed and removed from the
         * user's view. It can be used, for example, in the event that what
         * the notification pertains to is no longer relevant, or to cancel a
         * notification with no expiration time.
         *
         * The NotificationClosed signal is emitted by this method.
         *
         * If the notification no longer exists, an empty D-BUS Error message
         * is sent back.
         */
        [DBus (name = "CloseNotification")]
        public void close_notification (uint32 id) throws DBusError, IOError {
            NotificationWindow.instance.close_notification (id, true);
            control_center.close_notification (id, true);
            NotificationClosed (id, ClosedReasons.CLOSED_BY_CLOSENOTIFICATION);
        }

        /**
         * This message returns the information on the server. Specifically, the
         * server name, vendor, and version number.
         */
        [DBus (name = "GetServerInformation")]
        public void get_server_information (out string name,
                                            out string vendor,
                                            out string version,
                                            out string spec_version)
        throws DBusError, IOError {
            name = "SwayNotificationCenter";
            vendor = "ErikReider";
            version = Constants.VERSIONNUM;
            spec_version = "1.3";
        }

        /**
         * A completed notification is one that has timed out, or has been
         * dismissed by the user.
         *
         * The ID specified in the signal is invalidated before the signal
         * is sent and may not be used in any further communications
         * with the server.
         */
        public signal void NotificationClosed (uint32 id, uint32 reason);

        /**
         * This signal is emitted when one of the following occurs:
         *
         * - The user performs some global "invoking" action upon a
         * notification. For instance, clicking somewhere on the
         * notification itself.
         *
         * - The user invokes a specific action as specified in the original
         * Notify request. For example, clicking on an action button.
         *
         * #Note
         *
         * Clients should not assume the server will generate this signal.
         * Some servers may not support user interaction at all, or may not
         * support the concept of being able to "invoke" a notification.
         */
        public signal void ActionInvoked (uint32 id, string action_key);

        /**
         * This signal can be emitted before a ActionInvoked signal. It
         * carries an activation token that can be used to activate a toplevel.
         */
        public signal void ActivationToken (uint32 id, string activation_token);

        /** To be used by the non-standard "inline-reply" capability */
        public signal void NotificationReplied (uint32 id, string text);
    }
}
0707010000008D000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002C00000000SwayNotificationCenter-0.12.3/src/notiModel0707010000008E000081A4000000000000000000000001690B16EB00004A41000000000000000000000000000000000000003B00000000SwayNotificationCenter-0.12.3/src/notiModel/notiModel.valanamespace SwayNotificationCenter {
    public enum ClosedReasons {
        EXPIRED = 1,
        DISMISSED = 2,
        CLOSED_BY_CLOSENOTIFICATION = 3,
        UNDEFINED = 4;
    }

    public enum UrgencyLevels {
        LOW = 0,
        NORMAL = 1,
        CRITICAL = 2;

        public static UrgencyLevels from_value (int val) {
            switch (val) {
                case 0:
                    return LOW;
                case 1:
                    return NORMAL;
                case 2:
                    return CRITICAL;
                default:
                    return NORMAL;
            }
        }

        public uint8 to_byte () {
            return this;
        }

        public string to_string () {
            switch (this) {
                case LOW:
                    return "Low";
                case NORMAL:
                    return "Normal";
                case CRITICAL:
                    return "Critical";
            }
            return "Normal";
        }
    }

    public struct ImageData {
        int width;
        int height;
        int rowstride;
        bool has_alpha;
        int bits_per_sample;
        int channels;
        unowned uint8[] data;

        bool is_initialized;
    }

    public class Action : Object {
        public string identifier { get; construct set; }
        public string text { get; construct set; }

        public Action (string identifier, string text) {
            Object (identifier: identifier, text: text);
        }

        public string to_string () {
            if (identifier == null || text == null) {
                return "None";
            }
            return "Name: %s, Id: %s".printf (text, identifier);
        }
    }

    public class NotifyParams : Object {
        public uint32 applied_id { get; set; }
        public string app_name { get; set; }
        public uint32 replaces_id { get; set; }
        public string app_icon { get; set; }
        public Action ?default_action { get; set; }
        public string summary { get; set; }
        public string body { get; set; }
        public HashTable<string, Variant> hints { get; set; }
        public int expire_timeout { get; set; }
        public int64 time { get; set; } // Epoch in seconds

        // Hints: https://specifications.freedesktop.org/notification-spec/latest/hints.html
        /**
         * When set, a server that has the "action-icons" capability will attempt to
         * interpret any action identifier as a named icon. The localized display
         * name will be used to annotate the icon for accessibility purposes.
         * The icon name should be compliant with the Freedesktop.org Icon Naming Specification.
         */
        public bool action_icons { get; set; }
        /**
         * This is a raw data image format which describes the
         * width, height, rowstride, has alpha, bits per sample, channels
         * and image data respectively.
         */
        public ImageData image_data { get; set; }
        public ImageData icon_data { get; set; }
        public string image_path { get; set; }
        /**
         * This specifies the name of the desktop filename representing the calling program.
         * This should be the same as the prefix used for the application's .desktop file.
         * An example would be "rhythmbox" from "rhythmbox.desktop". This can be used
         * by the daemon to retrieve the correct icon for the application, for
         * logging purposes, etc.
         */
        public string desktop_entry { get; set; }
        /** The type of notification this is. */
        public string category { get; set; }
        /**
         * A themeable named sound from the freedesktop.org sound naming specification
         * to play when the notification pops up. Similar to icon-name, only for sounds.
         * An example would be "message-new-instant".
         */
        public string sound_name { get; set; }
        /** The path to a sound file to play when the notification pops up. */
        public string sound_file { get; set; }
        /**
         * When set the server will not automatically remove the notification
         * when an action has been invoked. The notification will remain resident
         * in the server until it is explicitly removed by the user or by the sender.
         * This hint is likely only useful when the server has the "persistence" capability.
         */
        public bool resident { get; set; }
        /**
         * When set the server will treat the notification as transient and by-pass
         * the server's persistence capability, if it should exist. (Always be visible)
         */
        public bool transient { get; set; }
        /** The urgency level. */
        public UrgencyLevels urgency { get; set; }
        /** Replaces the old notification with the same value of:
         * - x-canonical-private-synchronous
         * - synchronous
         */
        public string ?synchronous { get; set; }
        /** Used for notification progress bar (0->100) */
        public int value {
            get {
                return priv_value;
            }
            set {
                priv_value = value.clamp (0, 100);
            }
        }
        private int priv_value { private get; private set; }
        public bool has_synch { public get; private set; }

        /** Inline-replies */
        public Action ?inline_reply { get; set; }
        public string ?inline_reply_placeholder { get; set; }

        // Custom hints
        /** Disables scripting for notification */
        public bool swaync_no_script { get; set; }

        /** Always show the notification, regardless of dnd/inhibit state */
        public bool swaync_bypass_dnd { get; set; }

        public Array<Action> actions { get; set; }

        public DesktopAppInfo ?desktop_app_info = null;

        public string name_id;

        public string display_name;

        public NotifyParams (uint32 applied_id,
                             string app_name,
                             uint32 replaces_id,
                             string app_icon,
                             string summary,
                             string body,
                             string[] actions,
                             HashTable<string, Variant> hints,
                             int expire_timeout) {
            this.applied_id = applied_id;
            this.app_name = app_name;
            this.replaces_id = replaces_id;
            this.app_icon = Uri.unescape_string (app_icon);
            this.summary = summary;
            this.body = body;
            this.hints = hints;
            this.expire_timeout = expire_timeout;
            this.time = (int64) (get_real_time () * 0.000001);

            this.has_synch = false;

            parse_hints ();

            parse_actions (actions);

            // Try to get the desktop file
            string[] entries = {};
            if (desktop_entry != null) {
                entries += desktop_entry.replace (".desktop", "");
                entries += desktop_entry.down ().replace (".desktop", "");
            }
            if (app_name != null) {
                entries += app_name.replace (".desktop", "");
                entries += app_name.down ().replace (".desktop", "");
            }
            foreach (string entry in entries) {
                var app_info = new DesktopAppInfo ("%s.desktop".printf (entry));
                // Checks if the .desktop file actually exists or not
                if (app_info is DesktopAppInfo) {
                    desktop_app_info = app_info;
                    break;
                }
            }

            // Set name_id
            this.name_id = this.desktop_entry ?? this.app_name ?? "";

            // Set display_name
            string ?display_name = this.desktop_entry ?? this.app_name;
            if (desktop_app_info != null) {
                display_name = desktop_app_info.get_display_name ();
            }
            if (display_name == null || display_name.length == 0) {
                display_name = "Unknown";
            }
            this.display_name = display_name;
        }

        private void parse_hints () {
            foreach (var hint in hints.get_keys ()) {
                Variant hint_value = hints[hint];
                switch (hint) {
                    case "SWAYNC_NO_SCRIPT" :
                        if (hint_value.is_of_type (VariantType.INT32)) {
                            swaync_no_script = hint_value.get_int32 () == 1;
                        } else if (hint_value.is_of_type (VariantType.BOOLEAN)) {
                            swaync_no_script = hint_value.get_boolean ();
                        }
                        break;
                    case "SWAYNC_BYPASS_DND" :
                        if (hint_value.is_of_type (VariantType.INT32)) {
                            swaync_bypass_dnd = hint_value.get_int32 () == 1;
                        } else if (hint_value.is_of_type (VariantType.BOOLEAN)) {
                            swaync_bypass_dnd = hint_value.get_boolean ();
                        }
                        break;
                    case "value" :
                        if (hint_value.is_of_type (VariantType.INT32)) {
                            this.has_synch = true;
                            value = hint_value.get_int32 ();
                        }
                        break;
                    case "synchronous":
                    case "private-synchronous":
                    case "x-canonical-private-synchronous":
                        if (hint_value.is_of_type (VariantType.STRING)) {
                            synchronous = hint_value.get_string ();
                        }
                        break;
                    case "action-icons":
                        if (hint_value.is_of_type (VariantType.INT32)) {
                            action_icons = hint_value.get_int32 () == 1;
                        } else if (hint_value.is_of_type (VariantType.BOOLEAN)) {
                            action_icons = hint_value.get_boolean ();
                        }
                        break;
                    case "image-data":
                    case "image_data":
                    case "icon_data":
                        if (image_data.is_initialized) {
                            break;
                        }
                        var img_d = ImageData ();
                        // Read each value
                        // https://specifications.freedesktop.org/notification-spec/latest/ar01s05.html
                        img_d.width = hint_value.get_child_value (0).get_int32 ();
                        img_d.height = hint_value.get_child_value (1).get_int32 ();
                        img_d.rowstride = hint_value.get_child_value (2).get_int32 ();
                        img_d.has_alpha = hint_value.get_child_value (3).get_boolean ();
                        img_d.bits_per_sample = hint_value.get_child_value (4).get_int32 ();
                        img_d.channels = hint_value.get_child_value (5).get_int32 ();
                        // Read the raw image data
                        img_d.data = (uint8[]) hint_value.get_child_value (6).get_data ();

                        img_d.is_initialized = true;
                        if (hint == "icon_data") {
                            icon_data = img_d;
                        } else {
                            image_data = img_d;
                        }
                        break;
                    case "image-path":
                    case "image_path":
                        if (hint_value.is_of_type (VariantType.STRING)) {
                            image_path = Uri.unescape_string (hint_value.get_string ());
                        }
                        break;
                    case "desktop-entry":
                        if (hint_value.is_of_type (VariantType.STRING)) {
                            desktop_entry = hint_value.get_string ();
                        }
                        break;
                    case "category":
                        if (hint_value.is_of_type (VariantType.STRING)) {
                            category = hint_value.get_string ();
                        }
                        break;
                    case "sound-name":
                        if (hint_value.is_of_type (VariantType.STRING)) {
                            sound_name = hint_value.get_string ();
                        }
                        break;
                    case "sound-file":
                        if (hint_value.is_of_type (VariantType.STRING)) {
                            sound_file = hint_value.get_string ();
                        }
                        break;
                    case "resident":
                        if (hint_value.is_of_type (VariantType.INT32)) {
                            resident = hint_value.get_int32 () == 1;
                        } else if (hint_value.is_of_type (VariantType.BOOLEAN)) {
                            resident = hint_value.get_boolean ();
                        }
                        break;
                    case "transient":
                        if (hint_value.is_of_type (VariantType.INT32)) {
                            transient = hint_value.get_int32 () == 1;
                        } else if (hint_value.is_of_type (VariantType.BOOLEAN)) {
                            transient = hint_value.get_boolean ();
                        }
                        break;
                    case "urgency":
                        if (hint_value.is_of_type (VariantType.BYTE)) {
                            urgency = UrgencyLevels.from_value (hint_value.get_byte ());
                        }
                        break;
                    case "x-kde-reply-placeholder-text":
                        if (hint_value.is_of_type (VariantType.STRING)) {
                            inline_reply_placeholder = hint_value.get_string ();
                        }
                        break;
                }
            }
        }

        private void parse_actions (string[] actions) {
            Array<Action> parsed_actions = new Array<Action> ();
            if (actions.length <= 1 || actions.length % 2 != 0) {
                this.actions = parsed_actions;
                return;
            }

            // Find all the matching filters
            List<unowned ActionMatching> valid_matchers = new List<unowned ActionMatching> ();
            unowned OrderedHashTable<ActionMatching> filters =
                ConfigModel.instance.notification_action_filter;
            foreach (string key in filters.get_keys ()) {
                unowned ActionMatching matcher = filters[key];
                if (matcher.matches_notification (this)) {
                    valid_matchers.append (matcher);
                }
            }

            for (int i = 0; i < actions.length; i += 2) {
                Action action = new Action (actions[i], actions[i + 1]);

                // Filtering
                bool matches = false;
                foreach (unowned ActionMatching matcher in valid_matchers) {
                    if (matcher.matches_action (action)) {
                        matches = true;
                        break;
                    }
                }
                if (matches) {
                    continue;
                }

                if (action.text != null && action.identifier != null) {
                    string id = action.identifier.down ();
                    switch (id) {
                        case "default":
                            default_action = action;
                            break;
                        case "inline-reply":
                            if (action.text == "") {
                                action.text = "Reply";
                            }
                            inline_reply = action;
                            break;
                        default:
                            parsed_actions.append_val (action);
                            break;
                    }
                }
            }

            this.actions = parsed_actions;
        }

        public string to_string () {
            var params = new OrderedHashTable<string ?> ();

            params.insert ("applied_id", applied_id.to_string ());
            params.insert ("app_name", app_name);
            params.insert ("replaces_id", replaces_id.to_string ());
            params.insert ("app_icon", app_icon);
            params.insert ("default_action", default_action == null
                        ? null : default_action.to_string ());
            params.insert ("summary", summary);
            params.insert ("body", "\t" + body);
            string[] _hints = {};
            foreach (var key in hints.get_keys ()) {
                Variant v = hints[key];
                string data = "data";
                if (!key.contains ("image") && !key.contains ("icon")) {
                    data = v.print (true);
                }
                _hints += "\n\t%s: %s".printf (key, data);
            }
            params.insert ("hints", string.joinv ("", _hints));
            params.insert ("expire_timeout", expire_timeout.to_string ());
            params.insert ("time", "\t" + time.to_string ());
            params.insert ("found desktop file", (desktop_app_info != null).to_string ());
            params.insert ("applied app_id", name_id);
            params.insert ("display name", display_name);

            params.insert ("action_icons", action_icons.to_string ());
            params.insert ("image_data", image_data.is_initialized.to_string ());
            params.insert ("icon_data", icon_data.is_initialized.to_string ());
            params.insert ("image_path", image_path);
            params.insert ("desktop_entry", desktop_entry);
            params.insert ("category", category);
            params.insert ("sound_name", sound_name);
            params.insert ("sound_file", sound_file);
            params.insert ("resident", resident.to_string ());
            params.insert ("transient", transient.to_string ());
            params.insert ("urgency", urgency.to_string ());
            string[] _actions = {};
            foreach (var _action in actions.data) {
                _actions += "\n\t" + _action.to_string ();
            }
            params.insert ("actions", string.joinv ("", _actions));
            params.insert ("inline-reply", inline_reply == null
                        ? null : inline_reply.to_string ());

            string[] result = {};
            foreach (var k in params.get_keys ()) {
                string ?v = params[k];
                result += "%s:\t\t %s".printf (k, v);
            }
            return "\n" + string.joinv ("\n", result);
        }
    }
}
0707010000008F000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002F00000000SwayNotificationCenter-0.12.3/src/notification07070100000090000081A4000000000000000000000001690B16EB00007F14000000000000000000000000000000000000004100000000SwayNotificationCenter-0.12.3/src/notification/notification.valanamespace SwayNotificationCenter {
    public class NotificationCloseButton : Adw.Bin {
        Gtk.Revealer revealer;
        Gtk.Button button;

        construct {
            valign = Gtk.Align.START;
            // TODO: Configurable
            halign = Gtk.Align.END;

            revealer = new Gtk.Revealer () {
                transition_type = Gtk.RevealerTransitionType.CROSSFADE,
                reveal_child = false,
            };
            revealer.notify["child-revealed"].connect (() => {
                set_visible (revealer.reveal_child);
            });
            set_child (revealer);

            button = new Gtk.Button.from_icon_name ("swaync-close-symbolic") {
                has_frame = false,
                halign = Gtk.Align.CENTER,
                valign = Gtk.Align.CENTER,
            };
            button.add_css_class ("close-button");
            button.add_css_class ("circular");
            button.clicked.connect (click_cb);
            revealer.set_child (button);
        }

        private void click_cb () {
            clicked ();
        }

        public signal void clicked ();

        public void set_reveal (bool state) {
            if (state == revealer.reveal_child) {
                set_visible (state);
                return;
            }

            if (state) {
                set_visible (true);
            }
            revealer.set_reveal_child (state);
        }

        public void set_transition_duration (uint duration) {
            revealer.set_transition_duration (duration);
        }
    }

    public enum NotificationType { CONTROL_CENTER, POPUP }

    [GtkTemplate (ui = "/org/erikreider/swaync/ui/notification.ui")]
    public class Notification : Adw.Bin {
        [GtkChild]
        unowned Gtk.Revealer revealer;
        [GtkChild]
        unowned DismissibleWidget dismissible_widget;

        [GtkChild]
        unowned Gtk.Overlay base_widget;

        [GtkChild]
        unowned Gtk.Box default_action;

        [GtkChild]
        unowned Gtk.FlowBox alt_actions_box;

        /** The default_action gesture. Allows clicks while not in swipe gesture. */
        private Gtk.GestureClick gesture;
        /** Detects when hovering over the widget */
        private Gtk.EventControllerMotion motion_controller;

        [GtkChild]
        unowned Gtk.ProgressBar progress_bar;

        [GtkChild]
        unowned IterBox base_box;

        [GtkChild]
        unowned NotificationCloseButton close_button;

        [GtkChild]
        unowned Gtk.Label summary;
        [GtkChild]
        unowned Gtk.Label time;
        [GtkChild]
        unowned Gtk.Label body;
        [GtkChild]
        unowned Gtk.Image img;
        [GtkChild]
        unowned Gtk.Image img_app_icon;
        [GtkChild]
        unowned Gtk.Picture body_image;

        // Inline Reply
        [GtkChild]
        unowned Gtk.Box inline_reply_box;
        [GtkChild]
        unowned Gtk.Entry inline_reply_entry;
        [GtkChild]
        unowned Gtk.Button inline_reply_button;

        private bool default_action_down = false;
        private bool default_action_in = false;

        private int notification_body_image_height {
            get;
            default = ConfigModel.instance.notification_body_image_height;
        }
        private int notification_body_image_width {
            get;
            default = ConfigModel.instance.notification_body_image_width;
        }

        private uint timeout_id = 0;

        public bool is_timed { get; construct; default = false; }

        public NotifyParams param { get; private set; }
        public unowned NotiDaemon noti_daemon { get; construct; }

        public NotificationType notification_type {
            get;
            construct;
            default = NotificationType.POPUP;
        }

        public uint timeout_delay { get; construct; }
        public uint timeout_low_delay { get; construct; }
        public uint timeout_critical_delay { get; construct; }

        public int transition_time { get; construct; }

        public int number_of_body_lines { get; construct; default = 10; }

        public bool has_inline_reply { get; private set; default = false; }

        private static Regex code_regex;

        private static Regex tag_regex;
        private static Regex tag_unescape_regex;
        private static Regex img_tag_regex;
        private const string[] TAGS = { "b", "u", "i" };
        private const string[] UNESCAPE_CHARS = {
            "lt;", "#60;", "#x3C;", "#x3c;", // <
            "gt;", "#62;", "#x3E;", "#x3e;", // >
            "apos;", "#39;", // '
            "quot;", "#34;", // "
            "amp;" // &
        };

        private Notification () {}

        /** Show a non-timed notification */
        public Notification.regular (NotifyParams param,
                                     NotiDaemon noti_daemon,
                                     NotificationType notification_type) {
            Object (noti_daemon: noti_daemon,
                    notification_type: notification_type);
            this.param = param;
            build_noti ();
        }

        /** Show a timed notification */
        public Notification.timed (NotifyParams param,
                                   NotiDaemon noti_daemon,
                                   NotificationType notification_type,
                                   uint timeout,
                                   uint timeout_low,
                                   uint timeout_critical) {
            Object (noti_daemon: noti_daemon,
                    notification_type: notification_type,
                    is_timed: true,
                    timeout_delay: timeout,
                    timeout_low_delay: timeout_low,
                    timeout_critical_delay: timeout_critical,
                    number_of_body_lines: 5
            );
            this.param = param;
            build_noti ();
        }

        construct {
            try {
                code_regex = new Regex ("(?<= |^)(\\d{3}(-| )\\d{3}|\\d{4,8})(?= |$|\\.|,)",
                                        RegexCompileFlags.MULTILINE);
                string joined_tags = string.joinv ("|", TAGS);
                tag_regex = new Regex ("&lt;(/?(?:%s))&gt;".printf (joined_tags));
                string unescaped = string.joinv ("|", UNESCAPE_CHARS);
                tag_unescape_regex = new Regex ("&amp;(?=%s)".printf (unescaped));
                img_tag_regex = new Regex ("<img[^>]* src=((\"([^\"]*)\")|(\'([^\']*)\'))[^>]*>");
            } catch (Error e) {
                stderr.printf ("Invalid regex: %s", e.message);
            }

            // Build the default_action gesture
            gesture = new Gtk.GestureClick ();
            default_action.add_controller (gesture);
            gesture.set_touch_only (false);
            gesture.set_exclusive (true);
            gesture.set_button (0);
            gesture.set_propagation_phase (Gtk.PropagationPhase.BUBBLE);
            gesture.pressed.connect ((_gesture, _n_press, _x, _y) => {
                default_action_in = true;
                default_action_down = true;
            });
            gesture.released.connect ((gesture, _n_press, _x, _y) => {
                // Emit released
                if (!default_action_down) {
                    return;
                }
                default_action_down = false;
                if (default_action_in) {
                    // Close notification on middle and right button click
                    switch (gesture.get_current_button ()) {
                        default:
                        case Gdk.BUTTON_PRIMARY:
                            click_default_action ();
                            break;
                        case Gdk.BUTTON_MIDDLE:
                        case Gdk.BUTTON_SECONDARY:
                            this.close_notification ();
                            break;
                    }
                }

                Gdk.EventSequence ?sequence = gesture.get_current_sequence ();
                if (sequence == null) {
                    default_action_in = false;
                }
            });
            gesture.update.connect ((gesture, sequence) => {
                Gtk.GestureSingle gesture_single = (Gtk.GestureSingle) gesture;
                if (sequence != gesture_single.get_current_sequence ()) {
                    return;
                }

                int width = default_action.get_width ();
                int height = default_action.get_height ();
                double x, y;

                gesture.get_point (sequence, out x, out y);
                bool in_button = (x >= 0 && y >= 0 && x < width && y < height);
                if (default_action_in != in_button) {
                    default_action_in = in_button;
                }
            });
            gesture.cancel.connect ((_gesture, _sequence) => {
                if (default_action_down) {
                    default_action_down = false;
                }
            });

            this.transition_time = ConfigModel.instance.transition_time;

            ///
            /// Signals
            ///

            default_action.unmap.connect (() => default_action_in = false);

            close_button.clicked.connect (() => close_notification ());

            motion_controller = new Gtk.EventControllerMotion ();
            base_widget.add_controller (motion_controller);
            motion_controller.enter.connect ((event) => {
                close_button.set_reveal (true);
                remove_noti_timeout ();
            });
            motion_controller.leave.connect ((controller) => {
                close_button.set_reveal (false);
                add_notification_timeout ();
            });


            // Remove notification when it has been swiped
            dismissible_widget.dismissed.connect (() => {
                remove_noti_timeout ();
                try {
                    noti_daemon.manually_close_notification (
                        param.applied_id, false);
                } catch (Error e) {
                    critical ("Error: %s", e.message);
                }
            });

            Gtk.EventControllerKey reply_key_controller = new Gtk.EventControllerKey ();
            reply_key_controller.key_released.connect ((keyval, keycode, state) => {
                if (Gdk.keyval_name (keyval) == "Return") {
                    inline_reply_button.clicked ();
                }
            });
            inline_reply_entry.add_controller (reply_key_controller);
            inline_reply_button.clicked.connect (() => {
                string text = inline_reply_entry.get_text ().strip ();
                if (text.length == 0) {
                    return;
                }
                noti_daemon.NotificationReplied (param.applied_id, text);
                // Dismiss notification without activating Action
                action_clicked (null);
            });
        }

        private void build_noti () {
            this.body.set_wrap (true);
            this.body.set_wrap_mode (Pango.WrapMode.WORD_CHAR);
            this.body.set_natural_wrap_mode (Gtk.NaturalWrapMode.WORD);
            this.body.set_ellipsize (Pango.EllipsizeMode.END);

            this.summary.set_wrap (false);
            this.summary.set_text (param.summary ?? param.app_name);
            this.summary.set_ellipsize (Pango.EllipsizeMode.END);

            close_button.set_transition_duration (this.transition_time);

            this.revealer.set_transition_duration (this.transition_time);

            // Changes the swipe direction depending on the notifications X position
            switch (ConfigModel.instance.positionX) {
                case PositionX.LEFT :
                    dismissible_widget.set_gesture_direction (SwipeDirection.SWIPE_LEFT);
                    break;
                default:
                case PositionX.RIGHT:
                case PositionX.CENTER:
                    dismissible_widget.set_gesture_direction (SwipeDirection.SWIPE_RIGHT);
                    break;
            }

            if (this.progress_bar.visible = param.has_synch) {
                this.progress_bar.set_fraction (param.value * 0.01);
            }

            set_body ();
            set_icon ();
            set_inline_reply ();
            set_actions ();
            set_style_urgency ();

            Timeout.add (0, () => {
                revealer.set_reveal_child (true);
                return Source.REMOVE;
            });

            remove_noti_timeout ();
            if (is_timed) {
                add_notification_timeout ();
            }
        }

        private void set_body () {
            string text = param.body ?? "";

            this.body.set_lines (this.number_of_body_lines);

            // Reset state
            body_image.hide ();

            // Removes all image tags and adds them to an array
            if (text.length > 0) {
                try {
                    // Get src paths from images
                    string[] img_paths = {};
                    MatchInfo info;
                    if (img_tag_regex.match (text, 0, out info)) {
                        do {
                            if (info == null) {
                                break;
                            }

                            // Use the first capture group and remove the start and end quote
                            string result = info.fetch (1).strip ().slice (1, -1);

                            // Replaces "~/" with $HOME
                            if (result.index_of ("~/", 0) == 0) {
                                result = Environment.get_home_dir () +
                                    result.slice (1, result.length);
                            }
                            img_paths += result;
                        } while (info.next ());
                    }

                    // Remove all images
                    text = img_tag_regex.replace (text, text.length, 0, "");

                    // Set the image if exists and is valid
                    if (img_paths.length > 0) {
                        string img = Functions.uri_to_path (img_paths[0]);
                        File file = File.new_for_path (img);
                        if (img.length > 0 && file.query_exists ()) {
                            Gdk.Texture texture = Gdk.Texture.from_file (file);
                            body_image.set_paintable (texture);
                            body_image.set_can_shrink (true);
                            body_image.set_content_fit (Gtk.ContentFit.SCALE_DOWN);
                            body_image.width_request = notification_body_image_width;
                            body_image.height_request = notification_body_image_height;
                            // Fixes the Picture taking up too much space:
                            // https://gitlab.gnome.org/GNOME/gtk/-/issues/7092
                            Gtk.LayoutManager layout = new Gtk.CenterLayout ();
                            body_image.set_layout_manager (layout);
                            body_image.show ();
                        }
                    }
                } catch (Error e) {
                    stderr.printf (e.message);
                }
            }

            // Markup
            try {
                Pango.AttrList ?attr = null;
                string ?buf = null;
                try {
                    // Try parsing without any hacks
                    Pango.parse_markup (text, -1, 0, out attr, out buf, null);
                } catch (Error e) {
                    // Default to hack if the initial markup couldn't be parsed

                    // Escapes all characters
                    string escaped = Markup.escape_text (text);
                    // Replace all valid tags brackets with <,</,> so that the
                    // markup parser only parses valid tags
                    // Ex: &lt;b&gt;BOLD&lt;/b&gt; -> <b>BOLD</b>
                    escaped = tag_regex.replace (escaped, escaped.length, 0, "<\\1>");

                    // Unescape a few characters that may have been double escaped
                    // Sending "<" in Discord would result in "&amp;lt;" without this
                    // &amp;lt; -> &lt;
                    escaped = tag_unescape_regex.replace_literal (escaped, escaped.length, 0, "&");

                    // Turns it back to markup, defaults to original if not valid
                    Pango.parse_markup (escaped, -1, 0, out attr, out buf, null);
                }

                this.body.set_text (buf);
                if (attr != null) {
                    this.body.set_attributes (attr);
                }
            } catch (Error e) {
                stderr.printf ("Could not parse Pango markup %s: %s\n",
                               text, e.message);
                // Sets the original text
                this.body.set_text (text);
            }

            this.body.set_visible (this.body.get_text ().length > 0);
        }

        /** Returns the first code found, else null */
        private string ?parse_body_codes () {
            if (!ConfigModel.instance.notification_2fa_action) {
                return null;
            }
            string body = this.body.get_text ().strip ();
            if (body.length == 0) {
                return null;
            }

            MatchInfo info;
            var result = code_regex.match (body, RegexMatchFlags.NOTEMPTY, out info);
            string ?match = info.fetch (0);
            if (!result || match == null) {
                return null;
            }

            return Functions.filter_string (
                match.strip (), (c) => c.isdigit () || c.isspace ()).strip ();
        }

        public void click_default_action () {
            action_clicked (param.default_action);
        }

        public void click_alt_action (uint index) {
            if (!alt_actions_box.visible) {
                return;
            }

            unowned Gtk.Widget ?button = null;
            int i = 0;
            for (unowned Gtk.Widget ?child = alt_actions_box.get_first_child ();
                 child != null;
                 child = child.get_next_sibling ()) {
                if (!(child is Gtk.FlowBoxChild)) {
                    continue;
                }
                unowned Gtk.FlowBoxChild f_child = (Gtk.FlowBoxChild) child;
                if (i == index) {
                    button = f_child.child;
                    break;
                }
                i++;
            }

            if (button == null) {
                return;
            } else if (button is Gtk.Button) {
                ((Gtk.Button) button).clicked ();
                return;
            }
            // Backup if the above fails
            action_clicked (param.actions.index (index));
        }

        private void action_clicked (Action ?action) {
            noti_daemon.run_scripts (param, ScriptRunOnType.ACTION);
            if (action != null
                && action.identifier != null
                && action.identifier != "") {
                // Try getting a XDG Activation token so that the application
                // can request compositor focus
                string ?token = swaync_daemon.xdg_activation.get_token (this);
                if (token != null) {
                    noti_daemon.ActivationToken (param.applied_id, token);
                }

                noti_daemon.ActionInvoked (param.applied_id, action.identifier);
                if (ConfigModel.instance.hide_on_action) {
                    try {
                        swaync_daemon.set_visibility (false);
                    } catch (Error e) {
                        critical ("Error: %s\n", e.message);
                    }
                }
            }
            if (!param.resident) {
                close_notification ();
            }
        }

        private void set_style_urgency () {
            // Reset state
            base_box.remove_css_class ("low");
            base_box.remove_css_class ("normal");
            base_box.remove_css_class ("critical");

            switch (param.urgency) {
                case UrgencyLevels.LOW :
                    base_box.add_css_class ("low");
                    break;
                case UrgencyLevels.NORMAL :
                    default :
                    base_box.add_css_class ("normal");
                    break;
                case UrgencyLevels.CRITICAL :
                    base_box.add_css_class ("critical");
                    break;
            }
        }

        private void set_inline_reply () {
            // Reset state
            inline_reply_box.hide ();
            // Only show inline replies in popup notifications if the compositor
            // supports ON_DEMAND layer shell keyboard interactivity
            if (!ConfigModel.instance.notification_inline_replies
                || (ConfigModel.instance.layer_shell
                    && !swaync_daemon.has_layer_on_demand
                    && notification_type == NotificationType.POPUP)) {
                return;
            }
            if (param.inline_reply == null) {
                return;
            }

            has_inline_reply = true;

            inline_reply_box.show ();

            inline_reply_entry.set_placeholder_text (
                param.inline_reply_placeholder ?? "Enter Text");
            // Set reply Button sensitivity to disabled if Entry text is empty
            inline_reply_entry.bind_property (
                "text",
                inline_reply_button, "sensitive",
                BindingFlags.SYNC_CREATE,
                (binding, srcval, ref targetval) => {
                targetval.set_boolean (((string) srcval).strip ().length > 0);
                return true;
            },
                null);

            inline_reply_button.set_label (param.inline_reply.text ?? "Reply");
        }

        private void set_actions () {
            alt_actions_box.set_visible (false);

            // Remove all of the old alt actions
            for (unowned Gtk.Widget child = alt_actions_box.get_first_child ();
                 child != null;
                 child = alt_actions_box.get_first_child ()) {
                alt_actions_box.remove (child);
            }
            alt_actions_box.set_max_children_per_line (1);

            // Check for security codes
            string ?code = parse_body_codes ();

            // Display all of the actions
            if (param.actions.length > 0 || code != null) {
                alt_actions_box.set_visible (true);

                // Add "Copy code" Action if available and copy it to clipboard when clicked
                if (code != null && code.length > 0) {
                    Gtk.FlowBoxChild flowbox_child = new Gtk.FlowBoxChild ();
                    flowbox_child.add_css_class ("notification-action");
                    alt_actions_box.append (flowbox_child);

                    Gtk.Button action_button = new Gtk.Button.with_label (
                        "COPY \"%s\"".printf (code));
                    action_button.clicked.connect (() => {
                        // Copy to clipboard
                        get_clipboard ().set_text (code);
                        // Dismiss notification
                        action_clicked (null);
                    });
                    action_button.set_can_focus (false);
                    flowbox_child.set_child (action_button);
                }

                int max_children_per_line = 0;
                // Add notification specified actions
                foreach (var action in param.actions.data) {
                    max_children_per_line++;
                    Gtk.FlowBoxChild flowbox_child = new Gtk.FlowBoxChild ();
                    flowbox_child.add_css_class ("notification-action");
                    alt_actions_box.append (flowbox_child);

                    Gtk.Button action_button = new Gtk.Button.with_label (action.text);
                    action_button.clicked.connect (() => action_clicked (action));
                    action_button.set_can_focus (false);
                    flowbox_child.set_child (action_button);
                }
                alt_actions_box.set_max_children_per_line (max_children_per_line.clamp (1, 7));
            }
        }

        public void set_time () {
            if (ConfigModel.instance.relative_timestamps) {
                this.time.set_text (get_relative_time ());
            } else {
                this.time.set_text (get_iso8601_time ());
            }
        }

        private string get_relative_time () {
            string value = "";

            double diff = (get_real_time () * 0.000001) - param.time;
            double secs = diff / 60;
            double hours = secs / 60;
            double days = hours / 24;
            if (secs < 1) {
                value = "Now";
            } else if (secs >= 1 && hours < 1) {
                // 1m - 1h
                var val = Math.floor (secs);
                value = val.to_string () + " min";
                if (val > 1) {
                    value += "s";
                }
                value += " ago";
            } else if (hours >= 1 && hours < 24) {
                // 1h - 24h
                var val = Math.floor (hours);
                value = val.to_string () + " hour";
                if (val > 1) {
                    value += "s";
                }
                value += " ago";
            } else {
                // Days
                var val = Math.floor (days);
                value = val.to_string () + " day";
                if (val > 1) {
                    value += "s";
                }
                value += " ago";
            }
            return value;
        }

        private string get_iso8601_time () {
            var dtime = new DateTime.from_unix_local (param.time);
            return dtime.format_iso8601 ();
        }

        public void close_notification (bool is_timeout = false) {
            remove_noti_timeout ();
            this.revealer.set_reveal_child (false);
            Timeout.add (this.transition_time, () => {
                try {
                    noti_daemon.manually_close_notification (param.applied_id,
                                                             is_timeout);
                } catch (Error e) {
                    critical ("Error: %s", e.message);
                }
                return Source.REMOVE;
            });
        }

        public void replace_notification (NotifyParams new_params) {
            this.param = new_params;
            build_noti ();
        }

        private void set_icon () {
            img.clear ();
            img.set_visible (true);
            img_app_icon.clear ();
            img_app_icon.set_visible (true);

            Icon ?app_icon_name = null;
            string ?app_icon_uri = null;
            if (param.desktop_app_info != null) {
                app_icon_name = param.desktop_app_info.get_icon ();
            }
            if (param.app_icon != null && param.app_icon != "") {
                app_icon_uri = param.app_icon;
            }

            var image_visibility = ConfigModel.instance.image_visibility;
            if (image_visibility == ImageVisibility.NEVER) {
                img.set_visible (false);
                img_app_icon.set_visible (false);
                return;
            }

            int notification_icon_size =
                ConfigModel.instance.notification_icon_size.clamp (-1, int.MAX);
            if (notification_icon_size < 1) {
                notification_icon_size = -1;
            }
            img.set_pixel_size (notification_icon_size);
            img.height_request = notification_icon_size;
            img.width_request = notification_icon_size;
            int app_icon_size = (notification_icon_size / 3).clamp (-1, int.MAX);
            if (app_icon_size < 1) {
                app_icon_size = -1;
            }
            img_app_icon.set_pixel_size (app_icon_size);

            bool img_path_is_theme_icon = false;
            bool img_path_exists = File.new_for_uri (param.image_path ?? "").query_exists ();
            if (param.image_path != null && !img_path_exists) {
                // Check if it's not a URI
                img_path_exists = File.new_for_path (
                    param.image_path ?? "").query_exists ();

                // Check if it's a freedesktop.org-compliant icon
                if (!img_path_exists) {
                    unowned Gtk.IconTheme icon_theme =
                        Gtk.IconTheme.get_for_display (get_display ());
                    img_path_exists = icon_theme.has_icon (param.image_path);
                    img_path_is_theme_icon = img_path_exists;
                }
            }
            bool app_icon_exists = File.new_for_uri (app_icon_uri ?? "").query_exists ();
            if (app_icon_uri != null && !app_icon_exists) {
                // Check if it's not a URI
                app_icon_exists = File.new_for_path (app_icon_uri ?? "").query_exists ();
            }

            // Set the main image to the provided image
            if (param.image_data.is_initialized) {
                Functions.set_image_data (param.image_data, img);
            } else if (param.image_path != null &&
                       param.image_path != "" &&
                       img_path_exists) {
                Functions.set_image_uri (param.image_path, img,
                                         img_path_exists,
                                         img_path_is_theme_icon);
            } else if (param.icon_data.is_initialized) {
                Functions.set_image_data (param.icon_data, img);
            }

            if (img.storage_type == Gtk.ImageType.EMPTY) {
                // Get the app icon
                if (app_icon_uri != null) {
                    Functions.set_image_uri (app_icon_uri, img,
                                             app_icon_exists);
                } else if (app_icon_name != null) {
                    img.set_from_gicon (app_icon_name);
                } else if (image_visibility == ImageVisibility.ALWAYS) {
                    // Default icon
                    img.set_from_icon_name ("image-missing");
                } else {
                    img.set_visible (false);
                }
            } else {
                // We only set the app icon if the main image is set
                if (app_icon_uri != null) {
                    Functions.set_image_uri (app_icon_uri, img_app_icon,
                                             app_icon_exists);
                } else if (app_icon_name != null) {
                    img_app_icon.set_from_gicon (app_icon_name);
                }
            }
        }

        public void add_notification_timeout () {
            if (!this.is_timed) {
                return;
            }

            // Removes the previous timeout
            remove_noti_timeout ();

            uint timeout;
            switch (param.urgency) {
                case UrgencyLevels.LOW :
                    timeout = timeout_low_delay * 1000;
                    break;
                case UrgencyLevels.NORMAL:
                default:
                    timeout = timeout_delay * 1000;
                    break;
                case UrgencyLevels.CRITICAL:
                    // Critical notifications should not automatically expire.
                    // Ignores the notifications expire_timeout.
                    if (timeout_critical_delay == 0) {
                        return;
                    }
                    timeout = timeout_critical_delay * 1000;
                    break;
            }
            uint ms = param.expire_timeout > 0 ? param.expire_timeout : timeout;
            if (ms <= 0) {
                return;
            }
            timeout_id = Timeout.add (ms, () => {
                close_notification (true);
                return Source.REMOVE;
            });
        }

        public void remove_noti_timeout () {
            if (timeout_id > 0) {
                Source.remove (timeout_id);
                timeout_id = 0;
            }
        }
    }
}
07070100000091000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000003400000000SwayNotificationCenter-0.12.3/src/notificationGroup07070100000092000081A4000000000000000000000001690B16EB000060A8000000000000000000000000000000000000004B00000000SwayNotificationCenter-0.12.3/src/notificationGroup/notificationGroup.valanamespace SwayNotificationCenter {
    public class NotificationGroup : Gtk.ListBoxRow {
        const string STYLE_CLASS_URGENT = "critical";
        const string STYLE_CLASS_COLLAPSED = "collapsed";

        public string name_id;

        private NotificationCloseButton close_button;
        private DismissibleWidget dismissible;
        private ExpandableGroup group;
        private Gtk.Revealer revealer = new Gtk.Revealer ();
        private Gtk.Image app_icon;
        private Gtk.Label app_label;

        private Gtk.EventControllerMotion motion_controller;
        private Gtk.GestureClick gesture;
        private bool gesture_down = false;
        private bool gesture_in = false;

        private HashTable<uint32, bool> urgent_notifications
            = new HashTable<uint32, bool> (direct_hash, direct_equal);

        public signal void on_expand_change (bool state);

        public NotificationGroup (string name_id, string display_name) {
            this.name_id = name_id;
            add_css_class ("notification-group");

            dismissible = new DismissibleWidget ();
            dismissible.dismissed.connect (close_all_notifications);
            set_child (dismissible);

            Gtk.Overlay overlay = new Gtk.Overlay ();
            overlay.set_can_focus (false);
            dismissible.child = overlay;

            Gtk.Box box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
            box.set_hexpand (true);
            overlay.set_child (box);

            close_button = new NotificationCloseButton ();
            close_button.clicked.connect (close_all_notifications);
            close_button.add_css_class ("notification-group-close-button");
            overlay.add_overlay (close_button);

            revealer.set_transition_type (Gtk.RevealerTransitionType.SLIDE_UP);
            revealer.set_reveal_child (false);
            revealer.set_transition_duration (Constants.ANIMATION_DURATION);

            // Add top controls
            Gtk.Box controls_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 4);
            Gtk.Box end_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 4);
            end_box.set_halign (Gtk.Align.END);
            end_box.add_css_class ("notification-group-buttons");

            // Collapse button
            Gtk.Button collapse_button = new Gtk.Button.from_icon_name (
                "swaync-collapse-symbolic");
            collapse_button.add_css_class ("circular");
            collapse_button.add_css_class ("notification-group-collapse-button");
            collapse_button.set_halign (Gtk.Align.END);
            collapse_button.set_valign (Gtk.Align.CENTER);
            collapse_button.clicked.connect (() => {
                set_expanded (false);
                on_expand_change (false);
            });
            end_box.append (collapse_button);

            // Close all button
            Gtk.Button close_all_button = new Gtk.Button.from_icon_name (
                "swaync-close-symbolic");
            close_all_button.add_css_class ("circular");
            close_all_button.add_css_class ("notification-group-close-all-button");
            close_all_button.set_halign (Gtk.Align.END);
            close_all_button.set_valign (Gtk.Align.CENTER);
            close_all_button.clicked.connect (() => {
                close_all_notifications ();
                on_expand_change (false);
            });
            end_box.append (close_all_button);

            // Group name label
            Gtk.Box start_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 4);
            start_box.set_halign (Gtk.Align.START);
            start_box.set_hexpand (true);
            start_box.add_css_class ("notification-group-headers");
            // App Icon
            app_icon = new Gtk.Image ();
            app_icon.set_valign (Gtk.Align.CENTER);
            app_icon.add_css_class ("notification-group-icon");
            start_box.append (app_icon);
            // App Label
            app_label = new Gtk.Label (display_name);
            app_label.xalign = 0;
            app_label.set_ellipsize (Pango.EllipsizeMode.END);
            app_label.add_css_class ("title-1");
            app_label.add_css_class ("notification-group-header");
            start_box.append (app_label);

            controls_box.prepend (start_box);
            controls_box.append (end_box);
            revealer.set_child (controls_box);
            box.append (revealer);

            set_activatable (false);

            group = new ExpandableGroup (Constants.ANIMATION_DURATION, (state) => {
                revealer.set_reveal_child (state);

                // Change CSS Class
                if (parent != null) {
                    set_classes ();
                }
            });
            set_classes ();
            box.append (group);

            /*
             * Handling of group presses
             */
            gesture = new Gtk.GestureClick ();
            box.add_controller (gesture);
            gesture.set_touch_only (false);
            gesture.set_exclusive (true);
            gesture.set_button (Gdk.BUTTON_PRIMARY);
            gesture.set_propagation_phase (Gtk.PropagationPhase.CAPTURE);
            gesture.pressed.connect ((_gesture, _n_press, x, y) => {
                gesture_in = true;
                gesture_down = true;
            });
            gesture.released.connect ((gesture, _n_press, _x, _y) => {
                // Emit released
                if (!gesture_down) {
                    return;
                }
                gesture_down = false;
                if (gesture_in) {
                    bool single_noti = only_single_notification ();
                    if (!group.is_expanded && !single_noti) {
                        set_expanded (true);
                        on_expand_change (true);
                    }
                    group.set_sensitive (single_noti || group.is_expanded);
                }

                Gdk.EventSequence ?sequence = gesture.get_current_sequence ();
                if (sequence == null) {
                    gesture_in = false;
                }
            });
            gesture.update.connect ((gesture, sequence) => {
                Gtk.GestureSingle gesture_single = (Gtk.GestureSingle) gesture;
                if (sequence != gesture_single.get_current_sequence ()) {
                    return;
                }

                int width = get_width ();
                int height = get_height ();
                double x, y;

                gesture.get_point (sequence, out x, out y);
                bool intersects = (x >= 0 && y >= 0 && x < width && y < height);
                if (gesture_in != intersects) {
                    gesture_in = intersects;
                }
            });
            gesture.cancel.connect ((gesture, sequence) => {
                if (gesture_down) {
                    gesture_down = false;
                }
            });

            /*
             * Handling of group hover
             */
            motion_controller = new Gtk.EventControllerMotion ();
            this.add_controller (motion_controller);
            motion_controller.motion.connect ((event) => {
                close_button.set_reveal (!group.is_expanded && !only_single_notification ());
            });
            motion_controller.leave.connect ((controller) => {
                close_button.set_reveal (false);
            });
        }

        private void set_classes () {
            remove_css_class (STYLE_CLASS_COLLAPSED);
            if (!group.is_expanded) {
                if (!has_css_class (STYLE_CLASS_COLLAPSED)) {
                    add_css_class (STYLE_CLASS_COLLAPSED);
                }
            }
        }

        private void set_icon () {
            if (is_empty ()) {
                return;
            }

            unowned Notification first = (Notification) group.widgets.first ().data;
            unowned NotifyParams param = first.param;
            // Get the app icon
            Icon ?icon = null;
            if (param.desktop_app_info != null
                && (icon = param.desktop_app_info.get_icon ()) != null) {
                app_icon.set_from_gicon (icon);
                app_icon.show ();
            } else {
                app_icon.set_from_icon_name ("application-x-executable-symbolic");
            }
        }

        /// Returns if there's more than one notification
        public bool only_single_notification () {
            return group.widgets.nth_data (0) != null && group.widgets.nth_data (1) == null;
        }

        public void set_expanded (bool state) {
            group.set_expanded (state);
            group.set_sensitive (only_single_notification () || group.is_expanded);
            dismissible.set_can_dismiss (!state);
        }

        public bool toggle_expanded () {
            bool state = !group.is_expanded;
            set_expanded (state);
            return state;
        }

        public void add_notification (Notification noti) {
            if (noti.param.urgency == UrgencyLevels.CRITICAL) {
                urgent_notifications.insert (noti.param.applied_id, true);
                if (!has_css_class (STYLE_CLASS_URGENT)) {
                    add_css_class (STYLE_CLASS_URGENT);
                }
            }
            group.add (noti);
            if (!only_single_notification ()) {
                if (!group.is_expanded) {
                    group.set_sensitive (false);
                }
            } else {
                set_icon ();
            }
        }

        public void remove_notification (Notification noti) {
            urgent_notifications.remove (noti.param.applied_id);
            if (urgent_notifications.length == 0) {
                remove_css_class (STYLE_CLASS_URGENT);
            }
            group.remove (noti);
            if (only_single_notification ()) {
                set_expanded (false);
                on_expand_change (false);
            }
        }

        public List<weak Gtk.Widget> get_notifications () {
            return group.widgets.copy ();
        }

        public unowned Notification ?get_latest_notification () {
            return (Notification ?) group.widgets.last ().data;
        }

        public int64 get_time () {
            if (group.widgets.is_empty ()) {
                return -1;
            }
            return ((Notification) group.widgets.last ().data).param.time;
        }

        public bool get_is_urgent () {
            return urgent_notifications.length > 0;
        }

        public uint get_num_notifications () {
            return group.widgets.length ();
        }

        public bool is_empty () {
            return group.widgets.is_empty ();
        }

        public void close_all_notifications () {
            close_button.set_reveal (false);
            urgent_notifications.remove_all ();
            foreach (unowned Gtk.Widget widget in group.widgets) {
                var noti = (Notification) widget;
                if (noti != null) {
                    noti.close_notification (false);
                }
            }
        }

        public void update () {
            set_icon ();
            foreach (unowned Gtk.Widget widget in group.widgets) {
                var noti = (Notification) widget;
                if (noti != null) {
                    noti.set_time ();
                }
            }
        }

        public float get_relative_y (Gtk.Widget parent) {
            Graphene.Point point = Graphene.Point.zero ();
            Graphene.Point dest_point;
            compute_point (parent, point, out dest_point);
            return dest_point.y;
        }
    }

    private class ExpandableGroup : Gtk.Widget {
        const int NUM_STACKED_NOTIFICATIONS = 3;
        const int COLLAPSED_NOTIFICATION_OFFSET = 8;

        public bool is_expanded { get; private set; default = true; }

        private double animation_progress = 1.0;
        private double animation_progress_inv {
            get {
                return (1 - animation_progress);
            }
        }
        private Adw.TimedAnimation animation;
        private Adw.CallbackAnimationTarget animation_target;

        private unowned on_expand_change change_cb;

        public List<unowned Gtk.Widget> widgets = new List<unowned Gtk.Widget> ();

        public delegate void on_expand_change (bool state);

        public ExpandableGroup (uint animation_duration, on_expand_change change_cb) {
            base.set_can_focus (true);

            this.change_cb = change_cb;

            animation_target = new Adw.CallbackAnimationTarget (animation_value_cb);
            animation = new Adw.TimedAnimation (this, 1.0, 0.0,
                                                animation_duration,
                                                animation_target);
            animation.set_easing (Adw.Easing.EASE_IN_OUT_CUBIC);
            animation.done.connect (animation_done_cb);

            this.show ();

            set_expanded (false);
        }

        public void set_expanded (bool value) {
            if (is_expanded == value) {
                return;
            }
            is_expanded = value;

            animate (is_expanded ? 1 : 0);

            this.queue_resize ();

            change_cb (is_expanded);
        }

        public void add (Gtk.Widget widget) {
            widget.set_parent (this);
            widgets.append (widget);
        }

        public void remove (Gtk.Widget widget) {
            widget.unparent ();
            widgets.remove (widget);
            if (this.get_visible () && widget.get_visible ()) {
                queue_resize ();
            }
        }

        private Gtk.Allocation get_alloc (Gtk.Widget w) {
            Gtk.Allocation alloc = Gtk.Allocation ();
            Graphene.Rect bounds;
            w.compute_bounds (this, out bounds);

            alloc.width = w.get_width ();
            alloc.height = w.get_height ();
            alloc.x = (int) bounds.origin.x;
            alloc.y = (int) bounds.origin.y;
            return alloc;
        }

        public override Gtk.SizeRequestMode get_request_mode () {
            return Gtk.SizeRequestMode.HEIGHT_FOR_WIDTH;
        }

        protected override void measure (Gtk.Orientation orientation, int for_size,
                                         out int minimum, out int natural,
                                         out int minimum_baseline, out int natural_baseline) {
            minimum = 0;
            natural = 0;
            minimum_baseline = -1;
            natural_baseline = -1;

            if (orientation == Gtk.Orientation.HORIZONTAL) {
                return;
            }

            foreach (unowned Gtk.Widget widget in widgets) {
                if (widget != null && widget.get_visible ()) {
                    int widget_minimum_height = 0;
                    int widget_natural_height = 0;
                    widget.measure (orientation, for_size,
                                    out widget_minimum_height,
                                    out widget_natural_height,
                                    null, null);

                    minimum += widget_minimum_height;
                    natural += widget_natural_height;
                }
            }

            int target_natural_height;
            int target_minimum_height;
            get_height_for_latest_notifications (for_size, out target_minimum_height,
                                                 out target_natural_height);
            // TODO: Always use natural as minimum?
            // Fixes large (tall) Notification body Pictures
            minimum = (int) Functions.lerp (minimum,
                                            target_natural_height,
                                            animation_progress_inv);
            natural = (int) Functions.lerp (natural,
                                            target_natural_height,
                                            animation_progress_inv);
        }

        protected override void size_allocate (int alloc_width, int alloc_height, int baseline) {
            base.size_allocate (alloc_width, alloc_height, baseline);

            int length = (int) widgets.length ();
            if (length == 0) {
                return;
            }

            Gtk.Allocation allocation = get_alloc (this);
            allocation.width = alloc_width;
            allocation.height = alloc_height;

            Gtk.Allocation prev_allocation = Gtk.Allocation ();
            prev_allocation.y = allocation.y;

            // The height of the most recent notification
            unowned Gtk.Widget last = widgets.last ().data;
            int target_height = 0;

            last.measure (Gtk.Orientation.VERTICAL, allocation.width, null, out target_height, null,
                          null);

            for (int i = length - 1; i >= 0; i--) {
                unowned Gtk.Widget widget = widgets.nth_data (i);
                if (widget != null && widget.get_visible ()) {
                    int height;
                    widget.measure (Gtk.Orientation.VERTICAL, allocation.width,
                                    null, out height,
                                    null, null);

                    Gtk.Allocation alloc = Gtk.Allocation ();
                    alloc.x = allocation.x;
                    alloc.y = (int) (prev_allocation.y +
                                     animation_progress * prev_allocation.height);
                    alloc.width = allocation.width;
                    alloc.height = height;
                    // Expand smaller stacked notifications to the expected height
                    // But only when the animation has finished
                    if (target_height > height && !is_expanded && animation_progress == 0) {
                        alloc.height = target_height;
                    }

                    // Add the collapsed offset to only stacked notifications.
                    // Excludes notifications index > NUM_STACKED_NOTIFICATIONS
                    if (i < length - 1 && length - 1 - i < NUM_STACKED_NOTIFICATIONS) {
                        alloc.y += (int) (animation_progress_inv * COLLAPSED_NOTIFICATION_OFFSET);
                    }

                    prev_allocation = alloc;
                    Gsk.Transform transform = new Gsk.Transform ();
                    transform = transform.translate (Graphene.Point ().init (alloc.x, alloc.y));
                    widget.allocate (alloc.width, alloc.height, baseline, transform);

                    if (get_realized ()) {
                        widget.show ();
                    }
                }
                if (get_realized ()) {
                    widget.set_child_visible (true);
                }
            }
        }

        // Draw the widget
        protected override void snapshot (Gtk.Snapshot snapshot) {
            int length = (int) widgets.length ();
            if (length == 0) {
                return;
            }

            Graphene.Rect bounds = Graphene.Rect ();
            if (!compute_bounds (this, out bounds)) {
                return;
            }
            Gtk.Allocation alloc = get_alloc (this);
            int width = alloc.width;

            unowned Gtk.Widget latest = widgets.nth_data (length - 1);
            Gtk.Allocation latest_alloc = get_alloc (latest);

            for (int i = 0; i < length; i++) {
                // Skip drawing excess notifications
                if (!is_expanded &&
                    animation_progress == 0 &&
                    i < length - NUM_STACKED_NOTIFICATIONS) {
                    continue;
                }

                unowned Gtk.Widget widget = widgets.nth_data (i);
                Gtk.Allocation widget_alloc = get_alloc (widget);

                int height_diff = latest_alloc.height - widget_alloc.height;

                snapshot.save ();

                // Move down even more if the height is larger than the latest
                // in the stack (helps with only rendering the bottom portion)
                double translate_y = height_diff * animation_progress_inv;
                snapshot.translate (Graphene.Point ().init (0, (float) translate_y));

                // Scale down lower notifications in the stack
                if (i + 1 != length) {
                    double scale = double.min (
                        animation_progress + Math.pow (0.95, length - 1 - i), 1);
                    // Moves the scaled notification to the center of X and bottom y
                    snapshot.translate (Graphene.Point ().init (
                                            (float) ((widget_alloc.width - width * scale) * 0.5),
                                            (float) (widget_alloc.height * (1 - scale))));
                    snapshot.scale ((float) scale, (float) scale);
                }

                int lerped_y = (int) Functions.lerp (-height_diff, 0, animation_progress);
                lerped_y += (int) widget_alloc.y - alloc.y;
                int lerped_height = (int) Functions.lerp (latest_alloc.height,
                                                          widget_alloc.height,
                                                          animation_progress);
                // Clip to the size of the latest notification
                // (fixes issue where a larger bottom notification would
                // be visible above)
                Graphene.Rect clip_bounds = Graphene.Rect ().init (0f,
                                                                   (float) lerped_y,
                                                                   (float) width,
                                                                   (float) lerped_height);
                snapshot.push_clip (clip_bounds);

                // TODO: Fades from the bottom at 0.5 -> top at 0.0 opacity
                // Draw patterns on the notification
                // cr.push_group ();
                // widget.draw (cr);
                // if (i + 1 != length) {
                //// Draw Fade Gradient
                // cr.save ();
                // cr.translate (0, lerped_y);
                // cr.scale (1, lerped_height * 0.5);
                // cr.set_source (fade_gradient);
                // cr.rectangle (0, 0, width, lerped_height * 0.5);
                // cr.set_operator (Cairo.Operator.DEST_OUT);
                // cr.fill ();
                // cr.restore ();
                // }
                // cr.pop_group_to_source ();
                // cr.paint ();

                // Cross-fades in the non visible stacked notifications when expanded
                if (i < length - NUM_STACKED_NOTIFICATIONS) {
                    snapshot.push_opacity (1.5 * animation_progress);
                }
                snapshot_child (widget, snapshot);

                if (i < length - NUM_STACKED_NOTIFICATIONS) {
                    snapshot.pop (); // Cross-fade
                }
                snapshot.pop (); // Clip

                snapshot.restore ();
            }
        }

        /** Gets the collapsed height (first notification + stacked) */
        private void get_height_for_latest_notifications (int for_size,
                                                          out int minimum,
                                                          out int natural) {
            minimum = 0;
            natural = 0;

            int length = (int) widgets.length ();
            if (length == 0) {
                return;
            }

            unowned GLib.List<weak Gtk.Widget> last = widgets.last ();
            if (last != null) {
                unowned Gtk.Widget last_widget = widgets.last ().data;

                last_widget.measure (Gtk.Orientation.VERTICAL, for_size,
                                     out minimum, out natural,
                                     null, null);
            }

            int offset = (length - 1).clamp (0, NUM_STACKED_NOTIFICATIONS - 1)
                * COLLAPSED_NOTIFICATION_OFFSET;

            natural += offset;
        }

        void animation_value_cb (double progress) {
            this.animation_progress = progress;

            this.queue_resize ();
        }

        void animation_done_cb () {
            this.queue_allocate ();
        }

        void animate (double to) {
            animation.set_value_from (animation_progress);
            animation.set_value_to (to);
            animation.reset ();
            animation.play ();
        }
    }
}
07070100000093000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000003500000000SwayNotificationCenter-0.12.3/src/notificationWindow07070100000094000081A4000000000000000000000001690B16EB00003BEE000000000000000000000000000000000000004D00000000SwayNotificationCenter-0.12.3/src/notificationWindow/notificationWindow.valanamespace SwayNotificationCenter {
    [GtkTemplate (ui = "/org/erikreider/swaync/ui/notification_window.ui")]
    public class NotificationWindow : Gtk.ApplicationWindow {
        private static NotificationWindow ?window = null;
        /**
         * A NotificationWindow singleton due to a nasty notification
         * enter_notify_event bug where GTK still thinks that the cursor is at
         * that location after closing the last notification. The next notification
         * would sometimes automatically be hovered...
         * The only way to "solve" this is to close the window and reopen a new one.
         */
        public static NotificationWindow instance {
            get {
                if (window == null) {
                    window = new NotificationWindow ();
                } else if (!window.get_mapped () ||
                           !window.get_realized () ||
                           !(window.get_child () is Gtk.Widget)) {
                    window.destroy ();
                    window = new NotificationWindow ();
                }
                return window;
            }
        }

        public static bool is_null {
            get {
                return window == null;
            }
        }

        [GtkChild]
        unowned Gtk.ScrolledWindow scrolled_window;
        [GtkChild]
        unowned AnimatedList list;

        Gee.HashSet<uint32> inline_reply_notifications = new Gee.HashSet<uint32> ();

        private static string ?monitor_name = null;

        private const int MAX_HEIGHT = 600;

        private NotificationWindow () {
            Object (css_name : "notificationwindow");
            if (swaync_daemon.use_layer_shell) {
                if (!GtkLayerShell.is_supported ()) {
                    stderr.printf ("GTKLAYERSHELL IS NOT SUPPORTED!\n");
                    stderr.printf ("Swaync only works on Wayland!\n");
                    stderr.printf ("If running waylans session, try running:\n");
                    stderr.printf ("\tGDK_BACKEND=wayland swaync\n");
                    Process.exit (1);
                }
                GtkLayerShell.init_for_window (this);
                GtkLayerShell.set_namespace (this, "swaync-notification-window");
            }
            this.set_anchor ();

            // -1 should set it to the content size unless it exceeds max_height
            scrolled_window.set_min_content_height (-1);
            scrolled_window.set_max_content_height (
                int.max (ConfigModel.instance.notification_window_height, -1));
            scrolled_window.set_propagate_natural_height (true);

            // TODO: Make option
            list.use_card_animation = true;

            // set_resizable (false);
            default_width = ConfigModel.instance.notification_window_width;

            // Change output on config reload
            app.config_reload.connect ((old, config) => {
                string monitor_name = config.notification_window_preferred_output;
                if (old == null
                    || old.notification_window_preferred_output != monitor_name
                    || NotificationWindow.monitor_name != monitor_name) {
                    NotificationWindow.monitor_name = null;
                    set_anchor ();
                }
            });
        }

        protected override void size_allocate (int w, int h, int baseline) {
            base.size_allocate (w, h, baseline);

            // Set the input region to only be the size of the ScrolledWindow
            set_input_region ();
        }

        protected override void snapshot (Gtk.Snapshot snapshot) {
            // HACK: Fixes fully transparent windows not being mapped
            Gdk.RGBA color = Gdk.RGBA () {
                red = 0,
                green = 0,
                blue = 0,
                alpha = 0,
            };
            snapshot.append_color (color, Graphene.Rect.zero ());
            base.snapshot (snapshot);
        }

        private void set_anchor () {
            if (swaync_daemon.use_layer_shell) {
                GtkLayerShell.set_layer (this, ConfigModel.instance.layer.to_layer ());

                GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.BOTTOM, true);
                GtkLayerShell.set_anchor (this, GtkLayerShell.Edge.TOP, true);
                switch (ConfigModel.instance.positionX) {
                    case PositionX.LEFT:
                        GtkLayerShell.set_anchor (
                            this, GtkLayerShell.Edge.RIGHT, false);
                        GtkLayerShell.set_anchor (
                            this, GtkLayerShell.Edge.LEFT, true);
                        break;
                    case PositionX.CENTER:
                        GtkLayerShell.set_anchor (
                            this, GtkLayerShell.Edge.RIGHT, false);
                        GtkLayerShell.set_anchor (
                            this, GtkLayerShell.Edge.LEFT, false);
                        break;
                    default:
                        GtkLayerShell.set_anchor (
                            this, GtkLayerShell.Edge.LEFT, false);
                        GtkLayerShell.set_anchor (
                            this, GtkLayerShell.Edge.RIGHT, true);
                        break;
                }
                switch (ConfigModel.instance.positionY) {
                    default:
                    case PositionY.TOP:
                        scrolled_window.set_valign (Gtk.Align.START);
                        break;
                    case PositionY.CENTER:
                        scrolled_window.set_valign (Gtk.Align.CENTER);
                        break;
                    case PositionY.BOTTOM:
                        scrolled_window.set_valign (Gtk.Align.END);
                        break;
                }
            }

            list.animation_reveal_type = AnimatedListItem.RevealAnimationType.SLIDE;
            switch (ConfigModel.instance.positionX) {
                case PositionX.LEFT:
                    list.animation_child_type = AnimatedListItem.ChildAnimationType.SLIDE_FROM_LEFT;
                    break;
                case PositionX.CENTER:
                    list.animation_child_type = AnimatedListItem.ChildAnimationType.NONE;
                    break;
                default:
                case PositionX.RIGHT:
                    list.animation_child_type =
                        AnimatedListItem.ChildAnimationType.SLIDE_FROM_RIGHT;
                    break;
            }
            switch (ConfigModel.instance.positionY) {
                default:
                case SwayNotificationCenter.PositionY.TOP:
                case SwayNotificationCenter.PositionY.CENTER:
                    list.direction = AnimatedListDirection.TOP_TO_BOTTOM;
                    break;
                case SwayNotificationCenter.PositionY.BOTTOM:
                    list.direction = AnimatedListDirection.BOTTOM_TO_TOP;
                    break;
            }

            // Set the preferred monitor
            string ?monitor_name = ConfigModel.instance.notification_window_preferred_output;
            if (NotificationWindow.monitor_name != null) {
                monitor_name = NotificationWindow.monitor_name;
            }
            set_monitor (Functions.try_get_monitor (monitor_name));
        }

        private void set_input_region () {
            unowned Gdk.Surface ?surface = window.get_surface ();
            if (surface == null) {
                return;
            }

            Cairo.Region region = new Cairo.Region ();
            foreach (AnimatedListItem item in list.visible_children) {
                if (item.destroying) {
                    continue;
                }
                Graphene.Rect out_bounds;
                item.compute_bounds (this, out out_bounds);
                Cairo.RectangleInt item_rect = Cairo.RectangleInt () {
                    x = (int) out_bounds.get_x (),
                    y = (int) out_bounds.get_y (),
                    width = (int) out_bounds.get_width (),
                    height = (int) out_bounds.get_height (),
                };
                region.union_rectangle (item_rect);
            }

            // The input region should only cover each preview widget
            Graphene.Rect scrollbar_bounds;
            unowned Gtk.Widget scrollbar = scrolled_window.get_vscrollbar ();
            if (scrollbar.should_layout ()) {
                scrollbar.compute_bounds (this, out scrollbar_bounds);
                Cairo.RectangleInt rect = Cairo.RectangleInt () {
                    x = (int) scrollbar_bounds.get_x (),
                    y = (int) scrollbar_bounds.get_y (),
                    width = (int) scrollbar_bounds.get_width (),
                    height = (int) scrollbar_bounds.get_height (),
                };
                region.union_rectangle (rect);
            }

            surface.set_input_region (region);
        }

        public void change_visibility (bool value) {
            if (!value) {
                close_all_notifications ();
            } else {
                this.set_anchor ();
            }
        }

        /** Return true to remove notification, false to skip */
        public delegate bool remove_iter_func (Notification notification);

        /** Hides all notifications. Only invokes the close action when transient */
        public void close_all_notifications (remove_iter_func ?func = null) {
            inline_reply_notifications.clear ();
            if (!this.get_realized ()) {
                return;
            }
            foreach (unowned AnimatedListItem item in list.children) {
                if (item.destroying) {
                    continue;
                }
                Notification notification = (Notification) item.child;
                if (func == null || func (notification)) {
                    remove_notification (notification, notification.param.transient, false);
                }
            }

            close ();
        }

        private void remove_notification (Notification ?noti,
                                          bool dismiss,
                                          bool transition) {
            // Remove notification and its destruction timeout
            if (noti != null) {
                if (noti.has_inline_reply) {
                    inline_reply_notifications.remove (noti.param.applied_id);
                    if (inline_reply_notifications.size == 0
                        && swaync_daemon.use_layer_shell
                        && GtkLayerShell.get_keyboard_mode (this)
                        != GtkLayerShell.KeyboardMode.NONE) {
                        GtkLayerShell.set_keyboard_mode (
                            this, GtkLayerShell.KeyboardMode.NONE);
                    }
                }
                noti.remove_noti_timeout ();
                list.remove.begin (noti, transition, (obj, res) => {
                    if (list.remove.end (res)
                        && dismiss
                        && (!get_realized ()
                            || !get_mapped ()
                            || !(get_child () is Gtk.Widget)
                            || list.is_empty ())) {
                        Idle.add_once (() => {
                            close ();
                        });
                        return;
                    }
                    set_input_region ();
                });
            }
        }

        public void add_notification (NotifyParams param) {
            var noti = new Notification.timed (param,
                                               swaync_daemon.noti_daemon,
                                               NotificationType.POPUP,
                                               ConfigModel.instance.timeout,
                                               ConfigModel.instance.timeout_low,
                                               ConfigModel.instance.timeout_critical);
            if (noti.has_inline_reply) {
                inline_reply_notifications.add (param.applied_id);

                if (swaync_daemon.use_layer_shell &&
                    GtkLayerShell.get_keyboard_mode (this)
                    != GtkLayerShell.KeyboardMode.ON_DEMAND
                    && swaync_daemon.has_layer_on_demand) {
                    GtkLayerShell.set_keyboard_mode (
                        this, GtkLayerShell.KeyboardMode.ON_DEMAND);
                }
            }

            if (!get_visible () || !get_mapped () || !get_realized ()) {
                set_anchor ();
            }
            show ();

            list.append.begin (noti, (obj, res) => {
                return_if_fail (list.append.end (res) != null);
                set_input_region ();
            });
        }

        public void close_notification (uint32 id, bool dismiss) {
            foreach (unowned AnimatedListItem item in list.children) {
                if (item.destroying) {
                    continue;
                }
                var noti = (Notification) item.child;
                if (noti != null && noti.param.applied_id == id) {
                    remove_notification (noti, dismiss, true);
                    break;
                }
            }
        }

        public void replace_notification (uint32 id, NotifyParams new_params) {
            foreach (unowned AnimatedListItem item in list.children) {
                if (item.destroying) {
                    continue;
                }
                var noti = (Notification) item.child;
                if (noti != null && noti.param.applied_id == id) {
                    noti.replace_notification (new_params);
                    // Position the notification in the beginning/end of the list
                    // and scroll to the new item
                    list.move_to_beginning (noti, true);
                    return;
                }
            }

            // Display a new notification if the old one isn't visible
            debug ("Could not find floating notification to replace: %u", id);
            add_notification (new_params);
        }

        public uint32 ?get_latest_notification () {
            unowned AnimatedListItem ?item = list.get_first_item ();
            if (item == null || !(item.child is Notification)) {
                return null;
            }

            Notification noti = (Notification) item.child;
            return noti.param.applied_id;
        }

        public void latest_notification_action (uint32 action) {
            unowned AnimatedListItem ?item = list.get_first_item ();
            if (item == null || !(item.child is Notification)) {
                return;
            }

            Notification noti = (Notification) item.child;
            noti.click_alt_action (action);
            noti.close_notification ();
        }

        public void set_monitor (Gdk.Monitor ?monitor) {
            debug ("Setting monitor for ControlCenter: %s", Functions.monitor_to_string (monitor));
            NotificationWindow.monitor_name = monitor == null ? null : monitor.connector;
            GtkLayerShell.set_monitor (this, monitor);

            set_input_region ();
        }
    }
}
07070100000095000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000003300000000SwayNotificationCenter-0.12.3/src/orderedHashTable07070100000096000081A4000000000000000000000001690B16EB00000497000000000000000000000000000000000000004900000000SwayNotificationCenter-0.12.3/src/orderedHashTable/orderedHashTable.valanamespace SwayNotificationCenter {
    /** A regular GLib HashTable but preserves the order of inserted keys and values */
    public class OrderedHashTable<T> {
        private HashTable<string, T> hash_table;
        private List<string> order;

        public uint length {
            get {
                return hash_table.length;
            }
        }

        public OrderedHashTable () {
            hash_table = new HashTable<string, T> (str_hash, str_equal);
            order = new List<string> ();
        }

        public unowned T @get (string key) {
            return hash_table.get (key);
        }

        public void insert (owned string key, owned T value) {
            if (!hash_table.contains (key)) {
                order.append (key);
            }
            hash_table.insert (key, value);
        }

        public List<weak string> get_keys () {
            return order.copy ();
        }

        public bool lookup_extended (string lookup_key, out unowned string orig_key,
                                     out unowned T value) {
            return hash_table.lookup_extended (lookup_key, out orig_key, out value);
        }
    }
}
07070100000097000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002F00000000SwayNotificationCenter-0.12.3/src/swayncDaemon07070100000098000081A4000000000000000000000001690B16EB00003226000000000000000000000000000000000000004100000000SwayNotificationCenter-0.12.3/src/swayncDaemon/swayncDaemon.valanamespace SwayNotificationCenter {
    public struct Data {
        public bool dnd;
        public bool cc_open;
        public uint count;
        public bool inhibited;
    }

    [DBus (name = "org.erikreider.swaync.cc")]
    public class SwayncDaemon : Object {
        public NotiDaemon noti_daemon;
        public XdgActivationHelper xdg_activation;

        private GenericSet<string> inhibitors = new GenericSet<string> (str_hash, str_equal);
        public bool inhibited { get; set; default = false; }
        [DBus (visible = false)]
        public signal void inhibited_changed (uint length);

        private Array<BlankWindow> blank_windows = new Array<BlankWindow> ();

        // Only set on swaync start due to some limitations of GtkLayerShell
        [DBus (visible = false)]
        public bool use_layer_shell { get; private set; }
        [DBus (visible = false)]
        public bool has_layer_on_demand { get; private set; }

        public SwayncDaemon () {
            // Init noti_daemon
            this.use_layer_shell = ConfigModel.instance.layer_shell;
            this.has_layer_on_demand = use_layer_shell &&
                GtkLayerShell.get_protocol_version () >= 4;
            this.noti_daemon = new NotiDaemon (this);
            this.xdg_activation = new XdgActivationHelper ();
            Bus.own_name (BusType.SESSION, "org.freedesktop.Notifications",
                          BusNameOwnerFlags.NONE,
                          on_noti_bus_aquired,
                          () => {},
                          () => {
                stderr.printf (
                    "Could not acquire notification name. " +
                    "Please close any other notification daemon " +
                    "like mako or dunst\n");
                Process.exit (1);
            });

            noti_daemon.on_dnd_toggle.connect ((dnd) => {
                try {
                    subscribe_v2 (noti_daemon.control_center.notification_count (),
                                  dnd,
                                  get_visibility (),
                                  inhibited);
                } catch (Error e) {
                    stderr.printf (e.message + "\n");
                }
            });

            // Update on start
            try {
                subscribe_v2 (notification_count (),
                              get_dnd (),
                              get_visibility (),
                              inhibited);
            } catch (Error e) {
                stderr.printf (e.message + "\n");
            }

            monitors.items_changed.connect (monitors_changed);
            Idle.add_once (() => monitors_changed (0, 0, monitors.get_n_items ()));
        }

        private void on_noti_bus_aquired (DBusConnection conn) {
            try {
                conn.register_object (
                    "/org/freedesktop/Notifications", noti_daemon);
            } catch (IOError e) {
                stderr.printf ("Could not register notification service\n");
                Process.exit (1);
            }
        }

        private void monitors_changed (uint position, uint removed, uint added) {
            bool visible = noti_daemon.control_center.get_visibility ();

            for (uint i = 0; i < removed; i++) {
                unowned BlankWindow win = blank_windows.index (position + i);
                win.close ();
                blank_windows.remove_index (position + i);
            }

            for (uint i = 0; i < added; i++) {
                Gdk.Monitor monitor = (Gdk.Monitor) monitors.get_item (position + i);
                BlankWindow win = new BlankWindow (monitor);
                win.set_visible (visible);
                blank_windows.insert_val (position + i, win);
            }

            // Set preferred output
            try {
                set_cc_monitor (
                    ConfigModel.instance.control_center_preferred_output);
                set_noti_window_monitor (
                    ConfigModel.instance.notification_window_preferred_output);
            } catch (Error e) {
                critical (e.message);
            }
        }

        [DBus (visible = false)]
        public void show_blank_windows (Gdk.Monitor ?ref_monitor) {
            if (!use_layer_shell || !ConfigModel.instance.layer_shell_cover_screen) {
                return;
            }
            foreach (unowned BlankWindow win in blank_windows.data) {
                if (win.monitor != ref_monitor) {
                    win.show ();
                }
            }
        }

        [DBus (visible = false)]
        public void hide_blank_windows () {
            if (!use_layer_shell) {
                return;
            }
            foreach (unowned BlankWindow win in blank_windows.data) {
                win.hide ();
            }
        }

        /// DBus

        /** Gets subscribe data but in one call */
        [DBus (name = "GetSubscribeData")]
        public Data get_subscribe_data () throws Error {
            return Data () {
                       dnd = get_dnd (),
                       cc_open = get_visibility (),
                       count = notification_count (),
                       inhibited = is_inhibited (),
            };
        }

        /**
         * Called when Dot Not Disturb state changes, notification gets
         * added/removed, and when Control Center opens
         */
        public signal void subscribe_v2 (uint count, bool dnd, bool cc_open, bool inhibited);

        /**
         * Called when Dot Not Disturb state changes, notification gets
         * added/removed, Control Center opens, and when inhibitor state changes
         */
        [Version (deprecated = true, replacement = "SwayncDaemon.subscribe_v2")]
        public signal void subscribe (uint count, bool dnd, bool cc_open);

        /** Reloads the CSS file */
        public bool reload_css () throws Error {
            bool result = Functions.load_css (style_path);
            return result;
        }

        /** Reloads the config file */
        public void reload_config () throws Error {
            print ("\n");
            message ("Reloading config\n");
            ConfigModel.reload_config ();
            noti_daemon.control_center.add_widgets ();
        }

        /**
         * Changes `name` to `value`.
         *
         * If `write_to_file` is True, it will write to the users
         * config file (`~/.config/swaync/config.json`) or `path`
         * if it's a valid path. Otherwise the changes will only
         * apply to the current instance.
         */
        public void change_config_value (string name,
                                         Variant value,
                                         bool write_to_file = true,
                                         string ?path = null) throws Error {
            ConfigModel.instance.change_value (name,
                                               value,
                                               write_to_file,
                                               path);
        }

        /** Gets the controlcenter visibility */
        public bool get_visibility () throws DBusError, IOError {
            return noti_daemon.control_center.get_visibility ();
        }

        /** Closes latest popup notification */
        public void hide_latest_notifications (bool close)
        throws DBusError, IOError {
            noti_daemon.hide_latest_notification (close);
        }

        /** Closes all popup notifications */
        public void hide_all_notifications ()
        throws DBusError, IOError {
            noti_daemon.hide_all_notifications ();
        }

        /** Closes all popup and controlcenter notifications */
        public void close_all_notifications () throws DBusError, IOError {
            noti_daemon.close_all_notifications ();
        }

        /** Gets the current controlcenter notification count */
        public uint notification_count () throws DBusError, IOError {
            return noti_daemon.control_center.notification_count ();
        }

        /** Toggles the visibility of the controlcenter */
        public void toggle_visibility () throws DBusError, IOError {
            if (noti_daemon.control_center.toggle_visibility ()) {
                noti_daemon.set_noti_window_visibility (false);
            }
        }

        /** Sets the visibility of the controlcenter */
        public void set_visibility (bool visibility) throws DBusError, IOError {
            noti_daemon.control_center.set_visibility (visibility);
            if (visibility) {
                noti_daemon.set_noti_window_visibility (false);
            }
        }

        /** Toggles the current Do Not Disturb state */
        public bool toggle_dnd () throws DBusError, IOError {
            return noti_daemon.toggle_dnd ();
        }

        /** Sets the current Do Not Disturb state */
        public void set_dnd (bool state) throws DBusError, IOError {
            noti_daemon.set_do_not_disturb (state);
        }

        /** Gets the current Do Not Disturb state */
        public bool get_dnd () throws DBusError, IOError {
            return noti_daemon.get_do_not_disturb ();
        }

        /** Closes a specific notification with the `id` */
        public void close_notification (uint32 id) throws DBusError, IOError {
            noti_daemon.control_center.close_notification (id, true);
        }

        /** Activates the `action_index` action of the latest notification */
        public void latest_invoke_action (uint32 action_index)
        throws DBusError, IOError {
            noti_daemon.latest_invoke_action (action_index);
        }

        /**
         * Adds an inhibitor with the Application ID
         * (ex: "org.erikreider.swaysettings", "swayidle", etc...).
         *
         * @return  false if the `application_id` already exists, otherwise true.
         */
        public bool add_inhibitor (string application_id) throws DBusError, IOError {
            if (inhibitors.contains (application_id)) {
                return false;
            }
            inhibitors.add (application_id);
            inhibited = inhibitors.length > 0;
            inhibited_changed (inhibitors.length);
            subscribe_v2 (noti_daemon.control_center.notification_count (),
                          noti_daemon.dnd,
                          get_visibility (),
                          inhibited);
            return true;
        }

        /**
         * Removes an inhibitor with the Application ID
         * (ex: "org.erikreider.swaysettings", "swayidle", etc...).
         *
         * @return  false if the `application_id` doesn't exist, otherwise true
         */
        public bool remove_inhibitor (string application_id) throws DBusError, IOError {
            if (!inhibitors.remove (application_id)) {
                return false;
            }
            inhibited = inhibitors.length > 0;
            inhibited_changed (inhibitors.length);
            subscribe_v2 (noti_daemon.control_center.notification_count (),
                          noti_daemon.dnd,
                          get_visibility (),
                          inhibited);
            return true;
        }

        /** Get the number of inhibitors */
        public uint number_of_inhibitors () throws DBusError, IOError {
            return inhibitors.length;
        }

        /** Get if is inhibited */
        public bool is_inhibited () throws DBusError, IOError {
            return inhibited;
        }

        /** Clear all inhibitors */
        public bool clear_inhibitors () throws DBusError, IOError {
            if (inhibitors.length == 0) {
                return false;
            }
            inhibitors.remove_all ();
            inhibited = false;
            inhibited_changed (0);
            subscribe_v2 (noti_daemon.control_center.notification_count (),
                          noti_daemon.dnd,
                          get_visibility (),
                          inhibited);
            return true;
        }

        public bool set_cc_monitor (string name) throws DBusError, IOError {
            unowned Gdk.Monitor ?monitor = Functions.try_get_monitor (name);
            if (monitor == null) {
                return false;
            }

            noti_daemon.control_center.set_monitor (monitor);
            return true;
        }

        public bool set_noti_window_monitor (string name) throws DBusError, IOError {
            unowned Gdk.Monitor ?monitor = Functions.try_get_monitor (name);
            if (monitor == null) {
                return false;
            }

            NotificationWindow.instance.set_monitor (monitor);
            return true;
        }
    }
}
07070100000099000041ED000000000000000000000002690B16EB00000000000000000000000000000000000000000000002B00000000SwayNotificationCenter-0.12.3/src/underlay0707010000009A000081A4000000000000000000000001690B16EB000007E9000000000000000000000000000000000000003900000000SwayNotificationCenter-0.12.3/src/underlay/underlay.valapublic class Underlay : Gtk.Widget {
    protected unowned Gtk.Widget _child = null;
    public unowned Gtk.Widget child {
        get {
            return _child;
        }
        set {
            _child = value;
            set_children ();
        }
    }

    protected unowned Gtk.Widget _underlay_child = null;
    public unowned Gtk.Widget underlay_child {
        get {
            return _underlay_child;
        }
        set {
            _underlay_child = value;
            set_children ();
        }
    }

    private void set_children () {
        if (_underlay_child != null) {
            _underlay_child.unparent ();
            _underlay_child.insert_after (this, null);
        }

        if (_child != null) {
            _child.unparent ();
            _child.insert_after (this, _underlay_child);
        }
    }

    /*
     * Overrides
     */

    protected override Gtk.SizeRequestMode get_request_mode () {
        return Gtk.SizeRequestMode.HEIGHT_FOR_WIDTH;
    }

    protected override void measure (Gtk.Orientation orientation, int for_size,
                                     out int minimum, out int natural,
                                     out int minimum_baseline, out int natural_baseline) {
        minimum = 0;
        natural = 0;
        minimum_baseline = -1;
        natural_baseline = -1;

        int child_min, child_nat;

        if (!child.visible) {
            return;
        }

        child.measure (orientation, for_size,
                       out child_min, out child_nat, null, null);

        minimum = int.max (minimum, child_min);
        natural = int.max (natural, child_nat);
    }

    protected override void size_allocate (int width, int height, int baseline) {
        if (child != null && child.should_layout ()) {
            child.allocate (width, height, baseline, null);
        }

        if (underlay_child != null && underlay_child.should_layout ()) {
            underlay_child.allocate (width, height, baseline, null);
        }
    }
}
0707010000009B000081A4000000000000000000000001690B16EB000009D0000000000000000000000000000000000000003600000000SwayNotificationCenter-0.12.3/src/xdg_activation.valausing SwayNotificationCenter;
using XDG.Activation;

public class XdgActivationHelper : Object {
    private static Wl.RegistryListener registry_listener = Wl.RegistryListener () {
        global = registry_handle_global,
    };
    private Activation *xdg_activation = null;

    public XdgActivationHelper () {
        unowned Wl.Display wl_display = Functions.get_wl_display ();
        var wl_registry = wl_display.get_registry ();
        wl_registry.add_listener (registry_listener, this);

        if (wl_display.roundtrip () < 0) {
            return;
        }
    }

    ~XdgActivationHelper () {
        if (xdg_activation != null) {
            xdg_activation->destroy ();
        }
    }

    private void registry_handle_global (Wl.Registry wl_registry, uint32 name,
                                         string @interface, uint32 version) {
        if (@interface == "xdg_activation_v1") {
            xdg_activation = wl_registry.bind<Activation> (name, ref Activation.iface, version);
            if (xdg_activation == null) {
                GLib.warning ("Could not bind to xdg_activation_v1 iface!");
            }
        }
    }

    private static void handle_done (void *data, Token activation_token,
                                     string token) {
        Value *value = (Value *) data;
        value->set_string (token.dup ());
    }

    private const TokenListener TOKEN_LISTENER = {
        handle_done,
    };

    public string ?get_token (Gtk.Widget widget) {
        if (xdg_activation == null) {
            return null;
        }

        unowned Wl.Display wl_display = Functions.get_wl_display ();
        unowned Gtk.Root ?root = widget.get_root ();
        if (root == null) {
            warning ("GDK Window is null");
            return null;
        }
        unowned Wl.Surface wl_surface = Functions.get_wl_surface (root.get_surface ());

        Value token_value = Value (typeof (string));
        token_value.set_string (null);

        Token *token = xdg_activation->get_activation_token ();
        token->add_listener (TOKEN_LISTENER, &token_value);
        token->set_surface (wl_surface);
        token->commit ();
        while (wl_display.dispatch () >= 0 && token_value.get_string () == null) {
            // noop
        }
        token->destroy ();

        unowned string token_str = token_value.get_string ();
        if (token_str != null && token_str.length > 0) {
            return token_str.dup ();
        }
        return null;
    }
}
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!1417 blocks
openSUSE Build Service is sponsored by