Skip to content

Commit 834a79b

Browse files
authored
feat: 3.12.0 (#233)
* feat: 3.12.0 * debug: segfaults * feat: remove dnd meta * feat: queue * chore: meta
1 parent e330226 commit 834a79b

File tree

13 files changed

+259
-151
lines changed

13 files changed

+259
-151
lines changed

data/dev.geopjr.Collision.gresource.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@
1616
<file alias="dev.geopjr.Archives.svg" preprocess="xml-stripblanks">icons/dev.geopjr.Archives.svg</file>
1717
<file alias="dev.geopjr.Calligraphy.svg" preprocess="xml-stripblanks">icons/dev.geopjr.Calligraphy.svg</file>
1818
<file alias="dev.geopjr.Tuba.svg" preprocess="xml-stripblanks">icons/dev.geopjr.Tuba.svg</file>
19+
<file alias="dev.geopjr.Turntable.svg" preprocess="xml-stripblanks">icons/dev.geopjr.Turntable.svg</file>
1920
</gresource>
2021
</gresources>

data/dev.geopjr.Collision.json

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,6 @@
1919
"*.la"
2020
],
2121
"modules": [
22-
{
23-
"name": "livevent",
24-
"sources": [
25-
{
26-
"type": "git",
27-
"url": "https://github.com/libevent/libevent.git",
28-
"tag": "release-2.1.12-stable"
29-
}
30-
]
31-
},
32-
{
33-
"name": "libyaml",
34-
"sources": [
35-
{
36-
"type": "archive",
37-
"url": "https://github.com/yaml/libyaml/releases/download/0.2.5/yaml-0.2.5.tar.gz",
38-
"sha256": "c642ae9b75fee120b2d96c712538bd2cf283228d2337df2cf2988e3c02678ef4"
39-
}
40-
]
41-
},
4222
{
4323
"name": "collision",
4424
"buildsystem": "simple",
@@ -83,17 +63,17 @@
8363
{
8464
"type": "archive",
8565
"dest": "crystal/",
86-
"url": "https://github.com/crystal-lang/crystal/releases/download/1.18.0/crystal-1.18.0-1-linux-x86_64.tar.gz",
87-
"sha256": "799863dc1ac04d09e48747709c8dd6b5de53aa581814f8be784d36bef0bed97a",
66+
"url": "https://github.com/crystal-lang/crystal/releases/download/1.18.2/crystal-1.18.2-1-linux-x86_64.tar.gz",
67+
"sha256": "de73134563db840791bc85bacd4e7f1360dc9c04af6e32a9b104300c561716b6",
8868
"only_arches": [
8969
"x86_64"
9070
]
9171
},
9272
{
9373
"type": "archive",
9474
"dest": "crystal/",
95-
"url": "https://github.com/geopjr-forks/crystal-aarch64/releases/download/v1.17.1/crystal-1.17.1-1-linux-aarch64.tar.xz",
96-
"sha256": "ada1d1f8c852a8b1886ad85f190f0ac8abc1c38c8262d5736446ecfc3acbbd3b",
75+
"url": "https://github.com/geopjr-forks/crystal-aarch64/releases/download/v1.18.2/crystal-1.18.2-1-linux-aarch64.tar.xz",
76+
"sha256": "1d7f787247491a092f3a6812243af4c6730c00722400c314782a801d32eb9a49",
9777
"only_arches": [
9878
"aarch64"
9979
]

data/dev.geopjr.Collision.metainfo.xml.in

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@
4848
<color type="primary" scheme_preference="dark">#26a269</color>
4949
</branding>
5050
<releases>
51+
<release version="3.12.0" date="2026-01-14">
52+
<description translate="no">
53+
<ul>
54+
<li>Visual refinements to match the state of the art of GNOME apps</li>
55+
<li>Added the ability to read the real file path from inside the sandbox</li>
56+
<li>Updated GNOME Files extension</li>
57+
<li>Fixed segfaults under heavy parallel use by using a queue</li>
58+
<li>Fixed non-thread-safe methods used in multi-threaded contexts</li>
59+
<li>Updated dependencies</li>
60+
<li>Updated translations</li>
61+
</ul>
62+
</description>
63+
</release>
5164
<release version="3.11.0" date="2025-08-11">
5265
<description translate="no">
5366
<ul>
Lines changed: 50 additions & 0 deletions
Loading

data/ui/application.ui

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@
9595
</child>
9696
<property name="content">
9797
<object class="GtkStack" id="mainStack">
98+
<child>
99+
<object class="GtkDropTarget" id="mainDnd">
100+
<property name="actions">copy</property>
101+
<property name="formats">GdkFileList</property>
102+
</object>
103+
</child>
98104
<child>
99105
<object class="GtkStackPage">
100106
<property name="name">welcomer</property>
@@ -293,6 +299,12 @@
293299
<property name="tooltip-text" translatable="yes">Select Another File to Check Against</property>
294300
<property name="height-request">125</property>
295301
<property name="use-underline">1</property>
302+
<child>
303+
<object class="GtkDropTarget" id="compareDnd">
304+
<property name="actions">copy</property>
305+
<property name="formats">GdkFileList</property>
306+
</object>
307+
</child>
296308
<child>
297309
<object class="GtkBox">
298310
<property name="orientation">vertical</property>

shard.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: collision
2-
version: 3.11.0
2+
version: 3.12.0
33

44
authors:
55
- GeopJr <evan@geopjr.dev>

src/collision.cr

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,30 @@ require "gettext"
44
require "log"
55

66
module Collision
7+
@@activated = false
8+
9+
def self.activated
10+
@@activated
11+
end
12+
13+
def self.activated=(activated : Bool)
14+
@@activated = activated
15+
end
16+
17+
@@atomic : Atomic(Int32) = Atomic.new(0)
18+
19+
def self.atomic
20+
@@atomic.get
21+
end
22+
23+
def self.atomic_increase
24+
@@atomic.add(1)
25+
end
26+
27+
def self.atomic_decrease
28+
@@atomic.sub(1)
29+
end
30+
731
# Enable debug logs if debug build or --debug is passed.
832
# Also save a copy in memory for the About window troubleshooting
933
# section.
@@ -88,17 +112,9 @@ def activate_with_file(app : Adw::Application, file : Gio::File? = nil)
88112
{% end %}
89113

90114
window.present
91-
92-
# Setup actions
93-
Collision::Action::HashInfo.new(app)
94-
Collision::Action::About.new(app)
95-
Collision::Action::NewWindow.new(app)
96-
Collision::Action::Quit.new(app)
97-
Collision::Action::OpenFile.new(app).cb = ->window.on_open_btn_clicked
98-
app.set_accels_for_action("window.close", {"<Ctrl>W"})
99-
100115
Collision::LOGGER.debug { "Window activated" }
101116
Collision::LOGGER.debug { "Settings: #{window_settings}" }
117+
# Collision.activated = true
102118

103119
unless file.nil?
104120
Collision::LOGGER.debug { "Activating with file" }
@@ -130,18 +146,34 @@ end
130146
def open_files(app : Adw::Application, files : Enumerable(Gio::File))
131147
files.each do |file|
132148
next unless !(file_path = file.path).nil? && Collision::FileUtils.file?(file_path)
133-
activate_with_file(app, file)
149+
if Collision.activated
150+
GLib.idle_add do
151+
activate_with_file(app, file)
152+
false
153+
end
154+
else
155+
activate_with_file(app, file)
156+
end
134157
end
135158
end
136159

137-
app = Adw::Application.new("dev.geopjr.Collision", Gio::ApplicationFlags::HandlesOpen)
138-
139-
app.activate_signal.connect(->activate(Adw::Application))
140-
app.open_signal.connect(->open_with(Adw::Application, Enumerable(Gio::File), String))
160+
def startup(app : Adw::Application)
161+
# Setup actions
162+
Collision::Action::HashInfo.new(app)
163+
Collision::Action::About.new(app)
164+
Collision::Action::NewWindow.new(app)
165+
Collision::Action::Quit.new(app)
166+
app.set_accels_for_action("win.open-file", {"<Ctrl>O"})
167+
app.set_accels_for_action("window.close", {"<Ctrl>W"})
168+
end
141169

142170
# ARGV but without flags, passed to Application.
143-
clean_argv = [PROGRAM_NAME].concat(ARGV.reject { |x| x.starts_with?('-') })
144171
gtk = Fiber::ExecutionContext::Isolated.new("Gtk") do
172+
app = Adw::Application.new("dev.geopjr.Collision", Gio::ApplicationFlags::HandlesOpen)
173+
clean_argv = [PROGRAM_NAME].concat(ARGV.reject { |x| x.starts_with?('-') })
174+
app.startup_signal.connect(->startup(Adw::Application))
175+
app.activate_signal.connect(->activate(Adw::Application))
176+
app.open_signal.connect(->open_with(Adw::Application, Enumerable(Gio::File), String))
145177
app.run(clean_argv)
146178
end
147179
gtk.wait

src/collision/actions/about.cr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class Collision::Action::About < Collision::Action
2525
dialog.add_other_app("dev.geopjr.Archives", Gettext.gettext("Archives"), Gettext.gettext("Create and view web archives"))
2626
dialog.add_other_app("dev.geopjr.Calligraphy", Gettext.gettext("Calligraphy"), Gettext.gettext("Turn text into ASCII banners"))
2727
dialog.add_other_app("dev.geopjr.Tuba", Gettext.gettext("Tuba"), Gettext.gettext("Browse the Fediverse"))
28+
dialog.add_other_app("dev.geopjr.Turntable", Gettext.gettext("Turntable"), Gettext.gettext("Scrobble your music"))
2829

2930
dialog.present(@app.active_window)
3031
end

src/collision/actions/open_file.cr

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/collision/functions/checksum.cr

Lines changed: 29 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,6 @@ require "digest/crc32"
66
require "digest/adler32"
77
require "blake3"
88

9-
macro gen_digest
10-
{
11-
{% for title, digest in Collision::HASH_FUNCTIONS %}
12-
:{{title}} => Digest::{{digest.id}}.new,
13-
{% end %}
14-
}
15-
end
16-
179
module Collision
1810
def self.split_by_4(hash_str : String)
1911
i = 0
@@ -32,58 +24,57 @@ module Collision
3224
class Checksum
3325
@mt_context : Fiber::ExecutionContext::Parallel = Fiber::ExecutionContext::Parallel.new("worker-threads", 8)
3426
@s_context = Fiber::ExecutionContext::Concurrent.new("channel-receiver")
35-
@digest = gen_digest
36-
@channel = Channel(Tuple(Symbol, String)).new
37-
38-
def initialize
39-
end
4027

4128
def calculate(type : Symbol, filename : String) : String
42-
hash = @digest[type]
43-
hash.reset
29+
hash =
30+
{% begin %}
31+
case type
32+
{% for title, digest in Collision::HASH_FUNCTIONS %}
33+
when :{{title}} then Digest::{{digest.id}}.new
34+
{% end %}
35+
else
36+
raise "no digest for #{type}" # shouldn't be reached
37+
end
38+
{% end %}
4439
hash.file(filename).hexfinal.downcase
4540
end
4641

47-
def on_finished(res : Hash(Symbol, String), &block)
48-
yield res
42+
def initialize
4943
end
5044

5145
def generate(filename : String, progressbar : Gtk::ProgressBar? = nil, &block : Hash(Symbol, String) ->)
5246
hash_amount = Collision::HASH_FUNCTIONS.size
5347
progressbar.fraction = 0.0
5448
progressbar.text = sprintf(Gettext.gettext("%d of %d hashes calculated"), {0, hash_amount})
5549

50+
atomic = Atomic.new(0)
51+
step = 1/hash_amount
52+
res = Hash(Symbol, String).new
53+
channel = Channel(Tuple(Symbol, String)).new(hash_amount)
5654
Collision::HASH_FUNCTIONS.each_with_index do |hash_key, hash_value, i|
57-
proc = ->(fiber_no : Int32) do
58-
@mt_context.spawn do
59-
LOGGER.debug { "Spawned fiber #{hash_value}" }
60-
61-
hash_value = calculate(hash_key, filename)
62-
LOGGER.debug { "Finished fiber #{fiber_no + 1}/#{hash_amount}" }
63-
64-
@channel.send({hash_key, hash_value})
55+
@mt_context.spawn do
56+
LOGGER.debug { "Spawned fiber #{hash_value}" }
57+
LOGGER.debug { "Finished fiber #{i + 1}/#{hash_amount}" }
58+
atomic.add(1)
59+
GLib.idle_add do
60+
unless progressbar.nil?
61+
progressbar.fraction = Math.min(progressbar.fraction + step, 1.0)
62+
progressbar.text = sprintf(Gettext.gettext("%d of %d hashes calculated"), {atomic.get, hash_amount})
63+
end
64+
false
6565
end
66+
67+
channel.send({hash_key, calculate(hash_key, filename)})
6668
end
67-
proc.call(i)
6869
end
6970

7071
@s_context.spawn do
7172
res = Hash(Symbol, String).new
72-
step = 1/hash_amount
7373
hash_amount.times do |i|
74-
t_res = @channel.receive
74+
t_res = channel.receive
7575
res[t_res[0]] = t_res[1]
76-
77-
GLib.idle_add do
78-
unless progressbar.nil?
79-
progressbar.fraction = Math.min(progressbar.fraction + step, 1.0)
80-
progressbar.text = sprintf(Gettext.gettext("%d of %d hashes calculated"), {i + 1, hash_amount})
81-
end
82-
false
83-
end
8476
end
85-
86-
on_finished(res, &block)
77+
block.call(res)
8778
end
8879
end
8980
end

0 commit comments

Comments
 (0)