Skip to content

menu.conf: hide the Pause menu item while the player is paused#18153

Open
go4399 wants to merge 1 commit into
mpv-player:masterfrom
go4399:menu.conf
Open

menu.conf: hide the Pause menu item while the player is paused#18153
go4399 wants to merge 1 commit into
mpv-player:masterfrom
go4399:menu.conf

Conversation

@go4399

@go4399 go4399 commented Jun 19, 2026

Copy link
Copy Markdown

According to the hidden logic of the Play and Pause menu items defined in menu.conf, these two items should never appear at the same time.

But, while the player was paused, the context menu showed both Play and Pause items.

The correct behavior is to hide the Pause item while the player is paused.

@go4399

go4399 commented Jun 19, 2026

Copy link
Copy Markdown
Author

Before this PR, while the player was paused, the context menu showed both Play and Pause items.
image

After this PR, the Pause item is hidden while the player is paused.
image

According to the hidden logic of the Play and Pause menu items, these two items should never appear at the same time. But, while the player was paused, the context menu showed both Play and Pause items. The correct behavior is to hide the Pause item while the player is paused.
@kasper93

Copy link
Copy Markdown
Member

Could you briefly explain how the changes in this PR, changes the conditions and fixes the bug?

@go4399

go4399 commented Jun 20, 2026

Copy link
Copy Markdown
Author

Could you briefly explain how the changes in this PR, changes the conditions and fixes the bug?

How to reproduce this bug

Case 1: When playing a video using the command mpv.exe test.mp4, the context menu does not exhibit this bug.

Case 2: First run mpv.exe, then drag and drop test.mp4 onto the mpv window to play it. At this point, pause playback, and the context menu will show the bug.

I added the following debug items to menu.conf:

[debug]pause==true	cycle pause	checked=pause==true
[debug]pause==false	cycle pause	checked=pause==false
[debug]idle_active==true	cycle pause	checked=idle_active==true
[debug]idle_active==false	cycle pause	checked=idle_active==false
[debug](pause or idle_active)==true	cycle pause	checked=(pause or idle_active)==true
[debug](pause or idle_active)==false	cycle pause	checked=(pause or idle_active)==false
[debug](idle_active or pause)==true	cycle pause	checked=(idle_active or pause)==true
[debug](idle_active or pause)==false	cycle pause	checked=(idle_active or pause)==false

When paused in Case 1, the context menu displays as follows:
image
It's correct.

When paused in Case 2, the context menu displays as follows:
image

pause == true
idle_active == false
(pause or idle_active) == true
(idle_active or pause) == false

The evaluation result shows a puzzling error.

@kasper93

kasper93 commented Jun 20, 2026

Copy link
Copy Markdown
Member

I see. However, I think better fix would be in select.lua to make those comparisons consistent, instead of changing the order in menu.conf. Maybe @guidocella can help here.

@go4399

go4399 commented Jun 20, 2026

Copy link
Copy Markdown
Author

I see. However, I think better fix would be in select.lua to make those comparisons consistent, instead of changing the order in menu.conf. Maybe @guidocella can help here.

This bug might be related to Lua's short-circuit evaluation of the or operator, which leads to incomplete dependency tracking.

Lua's short-circuit evaluation for or works as follows: for a or b, if a is true, only a is evaluated and b is ignored. If a is false, both a and b are evaluated.

When hidden = idle_active or pause:

  • Case 1: When playing a video using the command mpv.exe test.mp4. The initial states are idle_active=false and pause=false. In select.lua, after compile_condition() is called in the parse_menu_item() function, the first call to evaluate_condition() evaluates both idle_active and pause. It calls magic_get(idle_active) and magic_get(pause), and property_items[] associates the idle_active and pause properties with the menu item "Pause", and uses mp.observe_property to track changes to both properties. Therefore, the context menu bug does not occur in this case.
  • Case 2: First run mpv.exe, then drag and drop test.mp4 onto the mpv window to play it. The initial states are idle_active=true and pause=false. In select.lua, the first call to evaluate_condition() in parse_menu_item() only evaluates idle_active and ignores pause, so property_items[] only associates the idle_active property with the menu item "Pause". Hence, the context menu bug occurs in this case.

When hidden = pause or idle_active:

  • Since pause is initially always false, in both Case 1 and Case 2, the first call to evaluate_condition() in parse_menu_item() evaluates both idle_active and pause. As a result, the context menu bug does not occur.

To verify this, I added several verbose statements to select.lua.

diff --git a/player/lua/select.lua b/player/lua/select.lua
index bdb061c..fa99a64 100644
--- a/player/lua/select.lua
+++ b/player/lua/select.lua
@@ -823,6 +823,10 @@ local have_dirty_items = false
 local current_item

 local function on_property_change(name, value)
+    if name == "pause" then
+        mp.msg.verbose("!!on_property_change(" .. name .. ", " .. tostring(value) .. ")")
+    end
+
     property_cache[name] = value

     if property_items[name] then
@@ -873,6 +877,7 @@ function _G.get(name, default)
         end

         property_items[name][current_item] = true
+        mp.msg.verbose("  property_items[" .. name .. "][" .. current_item.item.title .."] = true")
     end

     if property_cache[name] == nil then
@@ -883,7 +888,9 @@ function _G.get(name, default)
 end

 local function magic_get(name)
-    return get(name:gsub("_", "-"), nil)
+    local v = get(name:gsub("_", "-"), nil)
+    mp.msg.verbose("  magic_get(" .. name .. ")=  " .. tostring(v))
+    return v
 end

 local evil_magic = {}
@@ -939,6 +946,7 @@ local function evaluate_condition(chunk, chunkname)
         return false
     end

+    mp.msg.verbose("evaluate_condition(" .. chunkname .. ")=  " .. tostring(not not result))
     return not not result
 end

@@ -1253,6 +1261,7 @@ end

 local function update_state(item)
     for state, compiled_condition in pairs(item.compiled_conditions) do
+        mp.msg.verbose("update_state: evaluate_condition: " .. item.item.title .. ": " .. state)
         toggle_state(item.item.state, state,
                      evaluate_condition(compiled_condition, item.item.title))
     end
@@ -1312,6 +1321,7 @@ local function parse_menu_item(line)
         end

         item.compiled_conditions[state] = compile_condition(condition, tokens[1])
+        mp.msg.verbose("evaluate_condition: " .. tokens[1] .. ": " .. state)
         if evaluate_condition(item.compiled_conditions[state], tokens[1]) then
             table.insert(item.item.state, state)
         end

Run mpv.exe --log-file=case2.log, the log confirms that the first call to evaluate_condition(), when processing the Pause item, it only calls magic_get(idle_active) and does not call magic_get(pause), and property_items[] only associates the idle_active property with the menu item "Pause".

[   0.118][v][select] evaluate_condition: Pa&use: hidden
[   0.118][v][select]   property_items[idle-active][Pa&use] = true
[   0.118][v][select]   magic_get(idle_active)=  true
[   0.118][v][select] evaluate_condition(Pa&use)=  true

When pause property changed, it only updates the menu item "Play".

[   6.442][v][select] !!on_property_change(pause, true)
[   6.442][v][select] update_state: evaluate_condition: Pla&y: hidden
[   6.442][v][select]   magic_get(pause)=  true
[   6.442][v][select] evaluate_condition(Pla&y)=  false
[   6.442][v][select] update_state: evaluate_condition: Pla&y: disabled
[   6.442][v][select]   magic_get(idle_active)=  false
[   6.442][v][select] evaluate_condition(Pla&y)=  false

The simplest way to fix this bug is to modify menu.conf, or to have evaluate_condition() evaluate all properties on its first call in select.lua, so as to avoid the side effects caused by Lua's short‑circuit evaluation.

Maybe @guidocella can help confirm.
case2.log

@guidocella

Copy link
Copy Markdown
Contributor

I don't see how to force it to evaluate all properties the first time. auto_profiles.lua has the same issue, it works in most cases just because most properties are initially unavailable. I guess we can just swap the order.

@llyyr

llyyr commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Write a helper function to convert a to a bool. The behavior described only happens with data types other than bool, with bools false or true will return true as it should.

For this particular one, you can just write idle_active == "yes" or pause

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants