Dear Behdad,

>  buf = hb.buffer_create ()
> +class Debugger(object):
> +     def message (self, buf, font, msg, data, _x_what_is_this):
> +             print(msg)
> +             return True
> +debugger = Debugger()
> +hb.buffer_set_message_func (buf, debugger.message, 1, 0)
>  hb.buffer_add_utf8 (buf, text.encode('utf-8'), 0, -1)
>  hb.buffer_guess_segment_properties (buf)

Yippee. At last, a debug interface :) (Behdad reminds me that I have been 
asking this once per year for the last 4 years!). Thank you.

OK. Now to make a great debug interface!

There are two ways of doing a debug interface: Event driven and One shot. There 
are probably more, but those are the only two that come to mind now. One shot 
sends all the information needed to give all the debug information for a debug 
point in its message. This allows the debugger not to have to keep state, but 
just record the results and pass them on. Event driven sends, well, events to 
the debugger and requires the debugger to keep state.

While one shot seems more inviting and is more in line with what Graphite does. 
I think for harfbuzz, I would recommend an event based debugger, where you send 
debug events at the start and end of every lookup, at recursion, during initial 
reordering and shaping, at dotted circle insertion, etc. and have an enum of 
events and let the debugger work out what it wants to do with that information.

So, I would add an enum to the debug message to give a debug message event type.

One big question that always needs to be answered in the debugger is: where are 
we? Where in the buffer are we now processing. This is the idx field of the 
buffer. I don't think this is exposed in the public buffer interface. So it 
either needs to be exposed or passed as part of the debug message.

I suggest that rather than relying on a message to give the lookup number, that 
the lookup number be passed as a separate parameter (or in a struct or 
whatever). The lookup number can be overloaded based on event type. So we could 
have a starting high level phase event type and use the lookup to say whether 
that is initial shaping, GSUB, GPOS, etc. for example. Or we could have 
different event types for each one. That's up to you.

I think we need to send a message each shaper pause when the pause occurs.

For GPOS we need to be passing parameters like the two points in an attachment 
or the actual calculated offset in a pair or single adjustment. When doing 
classed based activities, we should be passing the class values involved or 
perhaps pointers (or offsets) to the data structures involved so that a 
debugger can turn cross reference that back to source code.

What does that look like now:

debug_message(type, buf, idx, lkupidx, void *aptr, void *bptr, msg, ...)

where aptr and bptr are defined by type and lkupidx and may point to things 
like an attachment point record or a lookup record in a class based contextual 
lookup or somesuch. They may also point to debugger specific data structures 
(perhaps for an attachment point one needs a pointer to the ap record and 2 
floats for the resolved x,y coordinates).

You know, if we get this right, we should be able to drop the msg, ... since 
debuggers really don't want to have to parse textual messages. Yes they are 
easy for a quick trace, but not for a real debugger. But it's welcome to stay 
to make such tracing programs' lives easier, but it shouldn't contain anything 
that isn't in the other parameters. If it does, then we need a way to pass it 
outside the message.

And yes, while I'm trying to define what the kitchen sink is, I'm also trying 
to keep this lightweight.

I know the moment I hit send, I'll think of things I've forgotten!

Yours,
Martin
_______________________________________________
HarfBuzz mailing list
HarfBuzz@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/harfbuzz

Reply via email to