SDL Event and UserEvent questions

This (SDL client) code will not compile. How to fix it?

# app.cr
class ColorPicker
  property color : UInt32
  def initialize(@color)
  end
end

picker = ColorPicker.new(0xFF0000FF)

enum UserEventType
  COLOR_PICKED
  DRAWING_CREATED
end

event = LibSDL::UserEvent.new(
  type: user_event_type,
  code: UserEventType::COLOR_PICKED,
  data1: pointerof(picker.color),
  data2: nil)

LibSDL.push_event(pointerof(event))
$ crystal app.cr

# In app.cr

# data1: pointerof(picker.color),
# Error: can't take address of picker.color

# LibSDL.push_event(pointerof(event))
# Error: argument 'event' of 'LibSDL#push_event' must be Pointer(LibSDL::Event), not Pointer(LibSDL::UserEvent)
# shard.yml
name: SDL
version: 0.1.0
dependencies:
  sdl-crystal-bindings:
    github: Hadeweka/SDL-Crystal-Bindings
    branch: sdl2v0_2_1

Hadeweka/SDL-Crystal-Bindings extract :

# sdl-crystal-bindings.cr
# ...
union Event
  type : UInt32
  common : CommonEvent
  display : DisplayEvent
  window : WindowEvent
  ...
  user : UserEvent
  ...
end
# ...
struct UserEvent
  type : UInt32
  timestamp : UInt32
  window_id : UInt32
  code : Int32
  data1 : Void*
  data2 : Void*
end
# ...
fun push_event = SDL_PushEvent(event : Event*) : LibC::Int
# ...

A UserEvent is not the same thing as an Event. You have to make it the correct type.

Perhaps the library could have done that work for you.

Also, picker.color is a call, so it doesn’t have anything a pointer could point at. pointerof only works with variables.
You might want to take the pointer of the instance variable @color: pointerof(picker.@color).

You might want to take the pointer of the instance variable @color: pointerof(picker.@color).

Got it, thanks. The Crystal reference didn’t make this obvious. Noted.

Where were you missing that in the docs? How can we improve it?

EDIT:
My goal was to obtain a pointer to the color UInt32 variable of the picker instance (expressed in C with &picker.x). I forgot that – correct me if I’m wrong – crystal struct and class instance vars are not accessible other than by methods. In that context, pointer_of(picker.color) doesn’t make any sense. What could be improved, if anything, would be the compilation error message. Something in the order of can't take address of method picker.color would have helped.

Also, placing the search box at the head of the topics list and filtering the latter by matching search results sections might help flatten the curve a bit. So much more about pointers exists than is found in the one pointer_of section of the topics list.


IGNORE:

I assumed that the return type of picker.color (indirectly created after declaring color a property) would be taken into account by the compiler, as opposed to compilation failing with a not-so-helpful can't take address of picker.color message. That left me on a chase before I saw your helpful answer.

Updated, functional code.

# app.cr
class ColorPicker
  property color : UInt32
  def initialize(@color)
  end
end

enum UserEventCode
  COLOR_PICKED
  DRAWING_CREATED
end

...

picker = ColorPicker.new(0xFF0000FF)
user_event_type : UInt32 = LibSDL.register_events(2)

...

ev = LibSDL::Event.new
ev.type = user_event_type
ev.user.code = UserEventCode::COLOR_PICKED
ev.user.data1 = pointerof(picker)
ev.user.data2 = nil

LibSDL.push_event(pointerof(ev))

This looks a bit odd. picker is already a pointer (because ColorPicker is a class, i.e. a reference type). Do you really want a double pointer here? This is now a pointer to the pointer of an instance of ColorPicker.
Maybe this should rather be pointer.as(Void*)?

I proposed improving the error message in Error message for `pointerof` should be more helpful · Issue #15865 · crystal-lang/crystal · GitHub

I don’t understand this part. Which documentation page are you referring to?

Thanks. I checked your proposal for change.

In the code below, both typeof(a.age) and typeof(pointerof(a.@age)) return UInt8 where typeof(pointerof(a.age)) fails.

Given a message of pointerof expects a variable or constant., I (newcomer) would check typeof(a.age) and, given a return value of UInt8, would still wonder why pointerof had failed.

Is there documentation on how compilation checks are done? Reading up on this would help me.

class Aged
  property age : UInt8 = 0
  def initialize(@age)
  end
  def age()
    @age
  end
end

a = Aged.new(42)
puts "a = Aged.new(42)"
puts typeof(a)                   # Aged
puts typeof(a.@age)              # UInt8
puts typeof(a.age)               # UInt8
puts typeof(pointerof(a))        # Pointer(Aged)
puts typeof(pointerof(a.@age))   # Pointer(UInt8)
# puts typeof(pointerof(a.age))  # Error: can't take address of a.age

age_ptr = pointerof(a.@age)
puts typeof(age_ptr)             # Pointer(UInt8)

I don’t understand this part. Which documentation page are you referring to?

I’ll send you some info more later. Work…

Thanks again.

typeof(pointerof(a.@age)) is Pointer(UInt8).

typeof(a)                 # => Aged
typeof(a.age)             # => UInt8
typeof(pointerof(a))      # => Pointer(Aged)
typeof(pointerof(a.@age)) # => Pointer(UInt8)

see Carcin

Not really. This happens all over the place in the compiler codebase. Whenever the compiler notices something is wrong while compiling the program, it errors.

The compiler code is all Crystal and relatively approachable (not in its entire complexity, but individual aspects are quite readable). If you grep for the error message, it’ll take you to crystal/src/compiler/crystal/semantic/main_visitor.cr at 96bf61e053eeb38036b8369e5ae030ec614fdc96 · crystal-lang/crystal · GitHub
I believe this example is quite clear, even without prior experience with the compiler: the visit(PointerOf) method calls pointerof_var which figures out whether a pointer can be taken, based on the type of the expression.

Thanks. It helps.

Incidentally, I had googled “Crystal SDL User event” a few days ago and both the original, not so helpful, answers provided by Leo (Brave browser) and Gemini (Chrome browser) AI assistants were automatically updated (tabs had remained open) this morning to show my (also in need of improvement) initial code.

Will post improved, final code here, for clarity’s sake.

# app.cr
enum UserEventCode
  DRAWING_CREATED
  COLOR_PICKED
end

class ColorPicker
  property color : UInt32
  def initialize(@color)
  end
end

picker = ColorPicker.new(0xFF0000FF)
user_event_type : UInt32 = LibSDL.register_events(1)

event = LibSDL::UserEvent.new(
  type: user_event_type,
  code: UserEventCode::COLOR_PICKED,
  data1: picker.as(Void*),
  data2: nil)

LibSDL.push_event(pointerof(event))