diff --git a/CHANGELOG.md b/CHANGELOG.md index edff876..9f6ff27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## [6.0.0-beta.8] XXXX-XX-XX +## Added +- Flag to disable branch toggle for distro-packaged releases +- Test whether script was sourced inline +- Commandline usage help text + +## Fixed +- ESC key destroying wait dialogs while thread is pending +- Tooltip signals being processed on main menu +- Leaky variable name in dialog title +- Prevent extraneous signals from propagating when column width is adjusted +- Script failing to start when remote endpoints are unavailable + +## Changed +- Optimize time cost of setup checks +- Location of notes file + ## [6.0.0-beta.7] 2025-09-20 ## Added - Users can save text notes to describe servers diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..4e302b8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,71 @@ +# Introduction + +Thank you for your interest in DZGUI! + +This guide goes over development conventions and best practices for contributors. +If you are a developer, you can skip to the end. + +# Requesting help + +If you encounter a problem with DZGUI, you can submit tickets on the GitHub +(issue tracker)[https://github.com/aclist/dzgui-devel/issues] under the +`troubleshooting` tag. + +# How can I help the project? + +There are several ways to help this project. +1. Report bugs that you find +2. Request features that you would like +3. Contribute features and fixes to the codebase +4. Contribute documentation to the project + +Before making any contribution, please read the `CODE_OF_CONDUCT.md` and act +accordingly. + +## Submitting a ticket + +Navigate to the GitHub (issue tracker)[https://github.com/aclist/dzgui-devel/issues]. +From there, follow the onscreen prompts. You will be asked questions such as: + +- What version are you using? +- What distribution are you using? +- What is the issue that you found? +- How can we reproduce the issue? + +You can also attach screenshots, logs, or other data that can help us. + +## Requesting a feature + +You can also request features via the same issue tracker. It is good practice to +first search for your idea to see if a similar one has already been posted. +If not, open a ticket where you describe your feature and its possible benefits. + +Please note that this is a community project, so it takes time for us to develop +features. Putting in a feature request does not mean that it will be implemented, +but we will do our best to support as many cool ideas as possible. + +## Contributing code or documentation + +If you would like to take ownership or assist with an issue on the issue tracker, or +contribute a new change, please follow the guidelines below. + +Fork this repository and check out the code up to `prerelease/`. +If there is no pending prerelease PR, you can base your changes off of +the `testing` branch. You may later be asked to retarget your PR to the prerelease +branch once a new one is made. This is because out-of-band PRs are consolidated into +the next release before merging. + +The following naming conventions apply for PRs: +- fix/ - patch/hotfix branches +- feat/ - feature branches +- doc/ - documentation branches +- infra/ - infrastructure branches + +Implement your changes and test them locally. If they work you may +open a merge request, and then we review your changes. If everything is OK, it +will be merged to `prerelease/`, then `testing`, and after a live +testing phase, to `stable` as well. + +It is recommended to follow +(Conventional Commits)[https://www.conventionalcommits.org/en/v1.0.0/], as this +integrates well with tooling and helps the project review your code. diff --git a/dzgui.sh b/dzgui.sh index 0e609a0..9aefe29 100755 --- a/dzgui.sh +++ b/dzgui.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -o pipefail -version=6.0.0.beta-7 +version=6.0.0.beta-8 #CONSTANTS aid=221100 @@ -63,17 +63,14 @@ testing_url="$url_prefix/testing" releases_url="https://github.com/$author/$repo/releases/download/browser" km_helper_url="$releases_url/latlon" - set_im_module(){ #TODO: drop pending SteamOS changes - pgrep -a gamescope | grep -q "generate-drm-mode" - if [[ $? -eq 0 ]]; then - GTK_IM_MODULE="" + if pgrep -a gamescope | grep -q "generate-drm-mode"; then + unset GTK_IM_MODULE logger INFO "Detected Steam Deck (Game Mode), unsetting GTK_IM_MODULE" - else - return fi } + redact(){ sed 's@\(/home/\)[^/]*@\1REDACTED@g' } @@ -87,13 +84,21 @@ logger(){ printf "%s␞%s␞%s::%s()::%s␞%s\n" "$date" "$tag" "$self" "$caller" "$line" "$string" \ | redact >> "$debug_log" } + setup_dirs(){ - for dir in "$state_path" "$cache_path" "$share_path" "$helpers_path" "$freedesktop_path" "$config_path" "$log_path"; do - if [[ ! -d $dir ]]; then - mkdir -p "$dir" - fi + directories=() + directories+=("$state_path") + directories+=("$cache_path") + directories+=("$share_path") + directories+=("$helpers_path") + directories+=("$freedesktop_path") + directories+=("$config_path") + directories+=("$log_path") + for dir in "${directories[@]}"; do + mkdir -p "$dir" done } + setup_state_files(){ if [[ -f "$debug_log" ]]; then rm "$debug_log" && touch $debug_log @@ -104,9 +109,8 @@ setup_state_files(){ logger INFO "Migrating legacy version file" fi # wipe cache files - local path="$cache_path" - if find "$path" -mindepth 1 -maxdepth 1 | read; then - for file in $path/*; do + if [[ $(ls -A "$cache_path") ]]; then + for file in "$cache_path"/*; do rm "$file" done logger INFO "Wiped cache files" @@ -221,8 +225,7 @@ END } depcheck(){ for dep in "${!deps[@]}"; do - command -v "$dep" 2>&1>/dev/null - if [[ $? -eq 1 ]]; then + if ! command -v "$dep" &> /dev/null; then local msg="Requires $dep >= ${deps[$dep]}" raise_error_and_quit "$msg" fi @@ -612,10 +615,10 @@ fetch_helpers_by_sum(){ [[ -f "$config_file" ]] && source "$config_file" declare -A sums sums=( - ["funcs"]="c27a81577c818b4f4d786ada52ad5042" + ["funcs"]="6ae3ead7034dc8e7543472bddee75c63" ["query_v2.py"]="55d339ba02512ac69de288eb3be41067" - ["servers.py"]="7f83d5c1ca54acb12f1bd6657feb2ecf" - ["ui.py"]="8d6e52b3e9032acbb84ba6cf137f455a" + ["servers.py"]="ed442c3aecf33f777d59dcf53650d263" + ["ui.py"]="8d05bbd7b8e15a97fae568e9a3dbf4d6" ["vdf2json.py"]="2f49f6f5d3af919bebaab2e9c220f397" ["pefile.py"]="21531f2c0d9dfa5f110cf6779f9d22c0" ) @@ -1117,13 +1120,61 @@ uninstall(){ rm "$self" echo "Uninstall routine complete" } + +usage(){ +cat <<- EOM +DZGUI - Free and Open Source DayZ launcher + +Usage: + + dzgui.sh [options] + +Description: + + When no option is provided, the script launches DZGUI. + +Options: + + -u, --uninstall: + Uninstalls the software + + -v, --version: + Prints the version + + -h, --help: + Prints this message +EOM +} + main(){ - local zenv=$(zenity --version 2>/dev/null) + # setup zenity environment + local zenv="" + zenv=$(zenity --version 2>/dev/null) [[ -z $zenv ]] && { echo "Requires zenity >= ${deps[$steamsafe_zenity]}"; exit 1; } - if [[ $1 == "--uninstall" ]] || [[ $1 == "-u" ]]; then - uninstall && - exit 0 - fi + + # parse arguments + while [[ $# -gt 0 ]]; do + case $1 in + "--uninstall" | "-u") + uninstall + exit 0 + # shift + ;; + "--version" | "-v") + echo $version + exit 0 + # shift + ;; + "--help" | "-h") + usage + exit 0 + # shift + ;; + *) + echo "Unrecognized command!" + return 1 + esac + done set_im_module @@ -1132,7 +1183,12 @@ main(){ printf "All OK. Kicking off UI...\n" python3 "$ui_helper" "--init-ui" "$version" "$is_steam_deck" + } -main "$@" -#TODO: tech debt: cruddy handling for steam forking -[[ $? -eq 1 ]] && pkill -f dzgui.sh + +if [[ $(basename "$0") == "dzgui.sh" ]]; then + main "$@" + + #TODO: tech debt: cruddy handling for steam forking + [[ $? -eq 1 ]] && pkill -f dzgui.sh +fi diff --git a/helpers/funcs b/helpers/funcs index 4b65df6..52c5409 100755 --- a/helpers/funcs +++ b/helpers/funcs @@ -1,6 +1,6 @@ #!/usr/bin/env bash set -o pipefail -version="6.0.0-beta.7" +version="6.0.0-beta.8" #CONSTANTS aid=221100 diff --git a/helpers/latlon.c b/helpers/latlon.c deleted file mode 100644 index 4c1788a..0000000 --- a/helpers/latlon.c +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include -#include - -#define R 6371 -#define TO_RAD (3.1415926536 / 180) -double dist(double th1, double ph1, double th2, double ph2) -{ - double dx, dy, dz; - ph1 -= ph2; - ph1 *= TO_RAD, th1 *= TO_RAD, th2 *= TO_RAD; - - dz = sin(th1) - sin(th2); - dx = cos(ph1) * cos(th1) - cos(th2); - dy = sin(ph1) * cos(th1); - return asin(sqrt(dx * dx + dy * dy + dz * dz) / 2) * 2 * R; -} - -int main(int argc, const char * argv[]) -{ - if(argc < 5 || argc > 5){ - return 1; - } - float coords[4]; - for(int i=1;i<5;i++){ - if(atof(argv[i]) == 0){ - return 1; - } - coords[i] = atof(argv[i]); - } - - double d = dist(coords[1], coords[2], coords[3], coords[4]); - printf("%.1f\n", d); - - return 0; -} diff --git a/helpers/servers.py b/helpers/servers.py index ef26b32..00cd129 100644 --- a/helpers/servers.py +++ b/helpers/servers.py @@ -87,6 +87,7 @@ def parse_json(json: list) -> list: except KeyError: continue + malformed = False for key in [ "map", "gametype", @@ -98,7 +99,11 @@ def parse_json(json: list) -> list: try: row[key] except KeyError: - continue + malformed = True + break + + if malformed is True: + continue try: r = row["gametype"].split(",") @@ -167,7 +172,7 @@ def parse_json(json: list) -> list: return rows -def query_direct(ip: str, qport: int, TIMEOUT=3.0) -> dict | None: +def query_direct(ip: str, qport: int, TIMEOUT: float=3.0) -> dict | None: try: info = a2s.info((ip, qport), TIMEOUT) @@ -440,7 +445,7 @@ def query_bm_api(api_key: str, bm_id: str) -> Record: raise BmAPIError("Failed to query Battlemetrics") -def validate_ip(addr: str): +def validate_ip(addr: str) -> Record: fields = addr.split(":") if len(fields) != 2: raise InvalidIpError("Address must be formatted as IP:Queryport") diff --git a/helpers/tools/.gitignore b/helpers/tools/.gitignore new file mode 100644 index 0000000..d8b458e --- /dev/null +++ b/helpers/tools/.gitignore @@ -0,0 +1 @@ +latlon diff --git a/helpers/tools/Makefile b/helpers/tools/Makefile new file mode 100644 index 0000000..38914fd --- /dev/null +++ b/helpers/tools/Makefile @@ -0,0 +1,24 @@ +bin := latlon + +CC = gcc + +CFLAGS += -Wall +CFLAGS += -Wextra +CFLAGS += -Wpedantic +CFLAGS += -std=c23 +CFLAGS += -O3 + +LDFLAGS += -lm + +PREFIX ?= $(HOME)/.local/share/dzgui + +all: $(bin) + +install: $(bin) + install -Dm 755 $(bin) $(PREFIX) + +$(bin): latlon.c + $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) + +clean: + rm -f $(bin) diff --git a/helpers/tools/latlon.c b/helpers/tools/latlon.c new file mode 100644 index 0000000..a734f31 --- /dev/null +++ b/helpers/tools/latlon.c @@ -0,0 +1,77 @@ +// This small program computes the distance between two points on the Earths +// surface. The algorithm used for computing is called haversine formula. +// Source for the algorithm can be found here: +// https://en.wikipedia.org/wiki/Haversine_formula + +#include +#include +#include + +// Earth's radius in meters. +#define RADIUS ((12756L / 2.0L) * 1000L) + +// PI natural constant +#define PI 3.1415926536L + +// coordinates struct for storing points +struct Coordinate { + double latitude; + double longitude; +}; + +/** + * @brief Calculates a radian value from an angle + */ +double radian_from(const double angle) { + return (angle * PI) / 180; +} + +/** + * @brief Calculate the haversine function from an angle in radian + */ +double haversine(const double angle) { return ((1.0L - cos(angle)) / 2); } + +/** + * @brief Calculate the distance between two coordinates + */ +double great_circle_distance(const struct Coordinate a, + const struct Coordinate b) { + + // calculate longitude and latitude differences + struct Coordinate diff_coord = { + .longitude = a.longitude - b.longitude, + .latitude = a.latitude - b.latitude, + }; + + // calculate haversine(theta) value, where theta is the angle between the + // two coordinates + double hav_theta = haversine(diff_coord.latitude) + + cos(a.latitude) * cos(b.latitude) * haversine(diff_coord.longitude); + + // calculate distance from radius, and haversine(theta) values + double distance = 2 * RADIUS * asin(sqrt(hav_theta)); + + return distance; +} + +int main(int argc, const char* argv[]) { + if (argc < 5 || argc > 5) { + return 1; + } + + const struct Coordinate a = { + .latitude = radian_from(atof(argv[1])), + .longitude = radian_from(atof(argv[2])), + }; + const struct Coordinate b = { + .latitude = radian_from(atof(argv[3])), + .longitude = radian_from(atof(argv[4])), + }; + + // Returns the distance in meters. + // To pretty-print the result in a consumer of this module, it is + // recommended to divide the result by 1,000 and round it. + printf("%.1f\n", great_circle_distance(a, b)); + + return 0; +} diff --git a/helpers/ui.py b/helpers/ui.py index e8c4906..9a53f5e 100644 --- a/helpers/ui.py +++ b/helpers/ui.py @@ -78,7 +78,7 @@ servers_path = f"{cache_path}/{app_name_abbr}.servers" config_path = f"{user_path}/.config/dztui" config_file = f"{config_path}/dztuirc" history_file = f"{state_path}/{app_name_abbr}.history" -notes_file = f"{state_path}/{app_name_abbr}.notes.json" +notes_file = f"{config_path}/{app_name_abbr}.notes.json" logger = logging.getLogger(__name__) log_file = f"{log_path}/{app_name}_DEBUG.log" @@ -1872,6 +1872,9 @@ class TreeView(Gtk.TreeView): ) -> bool: if self.is_server_context(self.view) is False: return + if self.subpage is None: + return + coords = widget.convert_widget_to_bin_window_coords(x, y) path = self.get_path_at_pos(coords.bx, coords.by) if path is None: @@ -2573,7 +2576,7 @@ class TreeView(Gtk.TreeView): App.right_panel.filters_vbox.set_active_combo(0) App.grid.right_panel.filters_vbox.set_visible(True) for column in self.get_columns(): - column.connect("notify::width", self._on_col_width_changed) + column.connect("notify::fixed-width", self._on_col_width_changed) App.grid.statusbar.update_server_meta() @@ -3207,8 +3210,11 @@ class GenericDialog(Gtk.MessageDialog): self.outer.set_margin_end(30) def _on_dialog_delete( - self, response_id: Gtk.ResponseType + self, response_id: Gtk.ResponseType, event: Gdk.Event ) -> Literal[True]: + """ + Prevent manual dialog destruction + """ return True def _return_to_main_menu(self, widget: Gtk.Widget) -> None: @@ -3232,7 +3238,7 @@ class LanDialog(Gtk.MessageDialog): buttons=Gtk.ButtonsType.OK_CANCEL, text="Scan LAN servers", secondary_text="Select the query port", - title="{appname}", + title=f"{app_name} - Dialog", modal=True, ) @@ -3758,7 +3764,7 @@ class LeftLabel(Gtk.Label): class Options(Gtk.Box): - def __init__(self): + def __init__(self, self_update=True): super().__init__( orientation=Gtk.Orientation.VERTICAL, margin_start=10, @@ -3835,11 +3841,18 @@ class Options(Gtk.Box): self.branch_combo.append_text("Testing") self.branch_combo.set_active(0) self.branch_combo.connect("changed", self._on_branch_changed) + self.branch_combo.set_sensitive(self_update) - msg = ( - "Stable: only contains stable features. " - "Testing: pre-release beta, contains new features." - ) + if self_update is True: + msg = ( + "Stable: only contains stable features. " + "Testing: pre-release beta, contains new features." + ) + else: + msg = ( + "In-app updates are disabled when installing " + "DZGUI via the system package manager." + ) eb = InfoEventBox(msg) version_rows = [ @@ -4355,7 +4368,7 @@ class Notebook(Gtk.Notebook): self.keys.show_all() self.append_page(self.keys) - self.settings = Options() + self.settings = Options(self_update=True) self.settings.type = RowType.OPTIONS self.settings.show_all() self.append_page(self.settings) diff --git a/images/tutorial/01.png b/images/tutorial/01.png deleted file mode 100644 index a8bb742..0000000 Binary files a/images/tutorial/01.png and /dev/null differ diff --git a/images/tutorial/02.png b/images/tutorial/02.png deleted file mode 100644 index 0aaf795..0000000 Binary files a/images/tutorial/02.png and /dev/null differ diff --git a/images/tutorial/03.png b/images/tutorial/03.png deleted file mode 100644 index 0a852ca..0000000 Binary files a/images/tutorial/03.png and /dev/null differ diff --git a/images/tutorial/04.png b/images/tutorial/04.png deleted file mode 100644 index 3f25729..0000000 Binary files a/images/tutorial/04.png and /dev/null differ diff --git a/images/tutorial/05.png b/images/tutorial/05.png deleted file mode 100644 index 9700a85..0000000 Binary files a/images/tutorial/05.png and /dev/null differ diff --git a/images/tutorial/06.png b/images/tutorial/06.png deleted file mode 100644 index 66bbbe7..0000000 Binary files a/images/tutorial/06.png and /dev/null differ diff --git a/images/tutorial/07.png b/images/tutorial/07.png deleted file mode 100644 index 1210080..0000000 Binary files a/images/tutorial/07.png and /dev/null differ diff --git a/images/tutorial/08.png b/images/tutorial/08.png deleted file mode 100644 index 2318770..0000000 Binary files a/images/tutorial/08.png and /dev/null differ diff --git a/images/tutorial/09.png b/images/tutorial/09.png deleted file mode 100644 index d2a1808..0000000 Binary files a/images/tutorial/09.png and /dev/null differ