RE: [fltk.general] A complete beginners question on call backs in Fluid

177 views
Skip to first unread message

MacArthur, Ian (Selex ES, UK)

unread,
Oct 20, 2014, 5:16:52 AM10/20/14
to fltkg...@googlegroups.com
> I've followed various tutorials on the web and the video on Fluid
> (Erco??) it's good but only relates to button presses.

> I have a Fluid window that looks like this

> main()
> Double_Window SomeWindow
> Dial SomeDial
> Value_Output ValueOfTheDial

> What I am trying to do is to get the Value_Output to update as the dial
> is changed so just what I do I put in the Dial's call back field.
> Ideally I would want integer values.

> I would rather stick to this general form of syntax as even my C skills
> are very rusty and don't want to go too C++ too soon.

> I'd be glad of any help received. I've tried everything I can think of
> based on the various tutorials but the better one in this area is code
> rather than fluid based. I hope to build up a general structure using
> Fluid and then add code to beef it out and gently enter the world of
> C++.

If you want to make *a lot* of dial and output combos, then you'd be better using fluid to derive your own widget (from Fl_Group) that combines a dial and an output in a reusable fashion,

If you just want a few, then something really simple like this ought to work...

=========================


# data file for the Fltk User Interface Designer (fluid)
version 1.0303
header_name {.h}
code_name {.cxx}
Function {} {open
} {
Fl_Window someWindow {
label {Dial Window Demo} open
private xywh {547 169 499 400} type Double visible
} {
Fl_Value_Output valueOfTheDial {
label {value:}
private xywh {90 146 45 24} box BORDER_BOX maximum 100 step 1
}
Fl_Dial someDial {
callback {int val = (int)o->value();

valueOfTheDial->value(val);} selected
private xywh {55 20 120 120} maximum 100 step 1
}
}
}





Selex ES Ltd
Registered Office: Sigma House, Christopher Martin Road, Basildon, Essex SS14 3EL
A company registered in England & Wales. Company no. 02426132
********************************************************************
This email and any attachments are confidential to the intended
recipient and may also be privileged. If you are not the intended
recipient please delete it from your system and notify the sender.
You should not copy it or use it for any purpose nor disclose or
distribute its contents to any other person.
********************************************************************

John Longer

unread,
Oct 21, 2014, 5:44:20 AM10/21/14
to fltkg...@googlegroups.com, ian.ma...@selex-es.com
Thank's. I had hoped that there was some simple incantation that could be added in the call back panel as there can be when text is displayed in an output widget each time a button is pressed as per one of the tutorial video's on the web. This sort of thing is  easy on qtcreator  via it's designer but I feel FLTK is more stable.

I think I will have a look at the separate widget way but that appears to leave me with the same problem - getting the output of the dial to display and remain static and global. From the above the simplest way to do that seems to be to create a dial widget and add code to display the reading.

John
-

 

John Longer

unread,
Oct 26, 2014, 10:17:36 AM10/26/14
to fltkg...@googlegroups.com, ian.ma...@selex-es.com
This does work but I wonder if some one could explain the mechanism in English. I've tried to avoid asking C++ specific questions but google is no help on this one.
 
When I load this into fluid having saved it as an fl file I see that the callback contains code. I had gained the impression that when I added a widget in fluid and went to it's C++ panel and added a name that this became it's variable name. I then used a callback to an output function -> value (variable name) I had "no known conversion to double" error messages. If I replaced the variable name with a literal double the output changed to show that when the dial was moved. Seems my assumption is incorrect? Additionally if I couted the variable name I obtained what looked like a pointer, if I couted *variable name I had the conversion error message again. Which leaves me wondering if this is a bug or just what it does point to.

Looking at the callback code that does work is see a variable declaration "int val" and then code I don't understand "= (int)o ->value()" then code I do understand "valueOfTheDial -> value(val)". What isn't clear is how this obtains the value the dial is set to. I tried to find out what (int)o etc meant on the web but no luck at all. val is declared as double in the dial cxx code but I would have thought this was out of this codes scope which makes things even less clear. :-) help. I found it was impossible to find the meaning of little o on the web but have seen it in other places as well.

John
-
 

John Longer

unread,
Oct 27, 2014, 5:27:02 AM10/27/14
to fltkg...@googlegroups.com, ian.ma...@selex-es.com


On Monday, October 20, 2014 10:16:52 AM UTC+1, MacArthur, Ian (Selex ES, UK) wrote:
One other aspect I missed out in the last post is that to make the code usable I have had to add a void function call to the end of the call back code. It takes the dial value and has no return value. It currently returns to the outer most widget as I have left that blank. At some point it will return to the code that does something about the dial setting change. I'm not clear if things will clear up correctly done this way. If for instance these were pure subroutines at a low level I would be leaving stuff on the stack.

The sort of code organisation I see is "initialisation - change settings - run a process continuously" so dial changes in real terms are exceptions that always go back to change settings. Hope my terminology is acceptable. I'm more used to dealing with "exceptions" via hardware interrupts usually in assembler..

John
-

Albrecht Schlosser

unread,
Oct 27, 2014, 6:18:28 AM10/27/14
to fltkg...@googlegroups.com
On 21.10.2014 11:44, John Longer wrote:

> On Monday, 20 October 2014 10:16:52 UTC+1, MacArthur, Ian (Selex ES, UK)
> wrote:

[fluid code and more removed]

> Thank's. I had hoped that there was some simple incantation that could
> be added in the call back panel as there can be when text is displayed
> in an output widget each time a button is pressed as per one of the
> tutorial video's on the web. This sort of thing is easy on qtcreator
> via it's designer but I feel FLTK is more stable.

It's as "simple" as adding some C++ statements in the "Callback:" input
in the C++ tab as you have found out already. There's no way in FLTK to
do it without C++ knowledge.

I'll reply in more detail to one of your other posts...

Albrecht Schlosser

unread,
Oct 27, 2014, 6:41:29 AM10/27/14
to fltkg...@googlegroups.com
On 26.10.2014 15:17, John Longer wrote:

> On Monday, October 20, 2014 10:16:52 AM UTC+1, MacArthur, Ian (Selex ES,
> UK) wrote:
>
...
> If you just want a few, then something really simple like this ought
> to work...
>
> =========================
>
>
> # data file for the Fltk User Interface Designer (fluid)
> version 1.0303
> header_name {.h}
> code_name {.cxx}
> Function {} {open
> } {
> Fl_Window someWindow {
> label {Dial Window Demo} open
> private xywh {547 169 499 400} type Double visible
> } {
> Fl_Value_Output valueOfTheDial {
> label {value:}
> private xywh {90 146 45 24} box BORDER_BOX maximum 100 step 1
> }
> Fl_Dial someDial {
> callback {int val = (int)o->value();
>
> valueOfTheDial->value(val);} selected
> private xywh {55 20 120 120} maximum 100 step 1
> }
> }
> }
>
>
>
> This does work but I wonder if some one could explain the mechanism
> in English. I've tried to avoid asking C++ specific questions but
> google is no help on this one.
>
> When I load this into fluid having saved it as an fl file I see that the
> callback contains code.

Yes, that's true. The code here is:

int val = (int)o->value();
valueOfTheDial->value(val);

> I had gained the impression that when I added a
> widget in fluid and went to it's C++ panel and added a name that this
> became it's variable name.

That's also true, but ... (see below).

> I then used a callback to an output function
> -> value (variable name) I had "no known conversion to double" error
> messages. If I replaced the variable name with a literal double the
> output changed to show that when the dial was moved. Seems my assumption
> is incorrect?

Probably, but without knowing what you really did (actual code) and how
you tried it, it's difficult to say.

> Additionally if I couted the variable name I obtained what
> looked like a pointer, if I couted *variable name I had the conversion
> error message again. Which leaves me wondering if this is a bug or just
> what it does point to.

See above.

> Looking at the callback code that does work is see a variable
> declaration "int val" and then code I don't understand "= (int)o
> ->value()" then code I do understand "valueOfTheDial -> value(val)".
> What isn't clear is how this obtains the value the dial is set to. I
> tried to find out what (int)o etc meant on the web but no luck at all.

You should not only load the .fl file into fluid, but also use fluid's
menu "File/Write Code..." to get a full working program. I assume you
did, since you wrote "... code that does work ...", but anyway. If you
look at the C++ tab of the "someDial" widget in fluid and hold the mouse
over the input field, you get a tooltip with text "... Use the variable
name 'o' to access the Widget pointer ...".

If you look at the generated .cxx file, you'll see the entire callback
function (reformatted slightly):

static void cb_someDial(Fl_Dial* o, void*) {
int val = (int)o->value();
valueOfTheDial->value(val);
}

Now you can see what 'o' is: the first argument of the callback
function, which is a pointer to the widget the callback was defined for.

The code you don't understand could be rewritten like:

int val = (int)( o->value() );

or more C++'ish:

int val = int( o->value() );

Note that it is not '(int)o' that is interesting. It's 'o->value()',
which is a double value that is cast to an int value and assigned to
val. That's pretty normal c/c++ code. The confusing thing is probably
only the argument 'o', which I explained above.

> val is declared as double in the dial cxx code but I would have thought
> this was out of this codes scope which makes things even less clear. :-)
> help.

Yep, 'val' in the "dial cxx code" has nothing to do with 'val' here.

> I found it was impossible to find the meaning of little o on the
> web but have seen it in other places as well.

You can't find anything useful regarding 'o', since this is just a
variable (or function argument) name in this context.

If you wrote the .cxx and .h files with "File/Write Code" you can
examine the full code and try to understand this. You can compile and
run it using

fltk-config --compile <filename>.cxx

where <filename> is the name part of your .fl file. I tried it, and it
worked for me (as expected).

Albrecht Schlosser

unread,
Oct 27, 2014, 7:01:31 AM10/27/14
to fltkg...@googlegroups.com
On 27.10.2014 10:27, John Longer wrote:

> One other aspect I missed out in the last post is that to make the code
> usable I have had to add a void function call to the end of the call
> back code.

No. The code works as-is. See my previous post.

> It takes the dial value and has no return value. It currently
> returns to the outer most widget as I have left that blank. At some
> point it will return to the code that does something about the dial
> setting change. I'm not clear if things will clear up correctly done
> this way. If for instance these were pure subroutines at a low level I
> would be leaving stuff on the stack.

I don't understand what you mean here. If you call a subroutine from
within a callback, it will eventually return to that callback. There's
no way that you can leave "stuff on the stack".

> The sort of code organisation I see is "initialisation - change settings
> - run a process continuously" so dial changes in real terms are
> exceptions that always go back to change settings. Hope my terminology
> is acceptable. I'm more used to dealing with "exceptions" via hardware
> interrupts usually in assembler..

The terminology is different, but the GUI code is similar to what you
describe and appear to be used to. The entire code is event-driven, once
you start the event loop with Fl::run().

There are ways to get to the internal events (by deriving own classes
and overriding the handle() method), but for simple tasks there is the
callback system that's much easier to use.

Callbacks are called by FLTK's event processing whenever something
_important_ happens, such as changing the value of a widget, clicking a
button, or selecting a menu. Mouse moves are not considered "important"
in this context.

The callback in Ian's example is called whenever you drag the dial,
because the value changes. It retrieves the current value and assigns it
to the "valueOfTheDial" (Fl_Value_Output) widget. After such an
assignment you would often have to call

valueOfTheDial->redraw();

to make the changes visible, but the value(...) method does this
internally for you.

You can extend the callback code by calling any function (e.g. log the
changes to a file) from the callback code. This function can be in a
different file, if you like.

I hope I could explain Ian's example a little for you. You should really
start reading the FLTK docs from the beginning to get more of a feeling
how all works, and you need to be familiar with C++ to work with FLTK.

Personal note: it is sometimes easier to understand what happens if you
don't use fluid, because this is another level of coding you need to
grok. Personally I never use fluid, but it is a useful tool for GUI
design, and once you know how it works, you can write entire (and more
complex) programs in fluid (if you like).

John Longer

unread,
Oct 27, 2014, 8:50:54 AM10/27/14
to fltkg...@googlegroups.com, ian.ma...@selex-es.com
Mmmm. Not sure if I should top or bottom post on here.

Thanks for the replies.

I tried all sorts of things with what I thought was the dials variable name using COUT and getting annoyed that I couldn't seem to get using namspace std to apply to all functions so had to keep retyping it for each cout as I was using it to test a number of things.. This included casting it etc. Thanks for the explanation of o I have found other information about that since asking - but not that one - its seems. I will get a grip of that eventually.

The reason that I asked about function returns is that I do not know how the system handles them. For instance if calling a function from within another destroys it what I have done wouldn't cause any problems. It's clear from what I have done that simply entering code in the call back area looks after itself. What wasn't clear is what happens if I then enter another function but it sounds like all will be ok. I asked the question because if have reworked the assembler output that some compilers can produce in the past so this area is a natural one for me to wonder about. There might have been some method of entering a function while destroying the one that was left. I do appreciate that there must be some mechanism to destroy it rather than just closing the window it's in. For all I know the size of the heap as PC people seem to call it might increase each time the dial setting is changed.

I'm finding that there is plenty of helpful FLTK example on the web but little really on doing the same things from Fluid. Naturally as these involve typing most examples make use of classes. These examples are far more helpful than the bulk of docs provided with FLTK/Fluid. I have done a limited amount of gui type work in the past so have a general dislike of laying them out directly in code. It's too tedious. Actually I retired early and haven't written any code of any sort for around 10 years now. I'm slightly amazed that I fancy doing any again ;-) and laying out several widgets directly in code would put me off immediately. Frankly I feel that that a visual approach is the only sensible way of handling this area. Fluid for several reason especially for maintenance concerns seems to be the most suitable OS candidate available. QtCreator/Designer looked good but it changes too often even with my limited playing about. Oddly it's also harder to get into than FLTK even though setting up signals is very easy. I've found that the docs can be out of date too. Not much fun for a beginner that has problems understanding them anyway. Must admit I feel there is too little if you want to do this type documentation about so some of the tutorial type web pages are very helpful.

John
-

On Monday, October 20, 2014 10:16:52 AM UTC+1, MacArthur, Ian (Selex ES, UK) wrote:

Albrecht Schlosser

unread,
Oct 27, 2014, 9:30:20 AM10/27/14
to fltkg...@googlegroups.com
On 27.10.2014 13:50, John Longer wrote:
> Mmmm. Not sure if I should top or bottom post on here.

Neither. We like your replies intermixed with (trimmed) citations, so
that one can see what you're talking about. See my posts (and this one)
for an example.

> Thanks for the replies.

You're welcome.

> I tried all sorts of things with what I thought was the dials variable
> name using COUT and getting annoyed that I couldn't seem to get using
> namspace std to apply to all functions so had to keep retyping it for
> each cout as I was using it to test a number of things.. This included
> casting it etc.

Without seeing your code it's hard to say what went wrong. Usually you
don't need any "namespace std" with FLTK 1.3 code, but maybe, since
you're using cout, YMMV.

> The reason that I asked about function returns is that I do not know how
> the system handles them. For instance if calling a function from within
> another destroys it what I have done wouldn't cause any problems.

I don't understand this. What/who destroys what?

> It's
> clear from what I have done that simply entering code in the call back
> area looks after itself. What wasn't clear is what happens if I then
> enter another function but it sounds like all will be ok.

Of course.

> I asked the
> question because if have reworked the assembler output that some
> compilers can produce in the past so this area is a natural one for me
> to wonder about. There might have been some method of entering a
> function while destroying the one that was left.

Yes, in assembler you can use all kinds of branches and jumps, but in
C/C++ there is only calling with a defined return. Unless you're using
setjmp/longjmp from the C standard library or exception handling in C++.
Both methods can leave something on the heap (allocated with malloc
and/or new, for instance), but nothing on the stack.

> I do appreciate that
> there must be some mechanism to destroy it rather than just closing the
> window it's in. For all I know the size of the heap as PC people seem to
> call it might increase each time the dial setting is changed.

Yes, there is a way to destroy a window, but that is (as discussed here
and elsewhere very often) not necessary if you close the window and then
exit the program. Memory will released anyway, and often faster than if
you called d'tors yourself.

There's (usually) nothing allocated when changing the value of a widget,
and if it was, the widget would (have to) take care of this. FLTK takes
care of this in the class destructors, and you shouldn't find real
memory leaks in FLTK itself. And hey, we're not "PC people" who don't
care about memory consumption. FLTK is the "Fast and Light Tool Kit" and
is intended to be used on memory-constrained embedded platforms.

> I'm finding that there is plenty of helpful FLTK example on the web but
> little really on doing the same things from Fluid. Naturally as these
> involve typing most examples make use of classes. These examples are far
> more helpful than the bulk of docs provided with FLTK/Fluid. I have done
> a limited amount of gui type work in the past so have a general dislike
> of laying them out directly in code. It's too tedious. Actually I
> retired early and haven't written any code of any sort for around 10
> years now. I'm slightly amazed that I fancy doing any again ;-) and
> laying out several widgets directly in code would put me off
> immediately. Frankly I feel that that a visual approach is the only
> sensible way of handling this area.

Yes, that's what fluid is, the "FLtk User Interface Designer" tool. Feel
free to use it - my FLTK usage has been very differently in the past, so
I wrote my personal note in the previous message. There's nothing wrong
with using fluid though. The main point I wanted to say was that it's
difficult to learn if you don't know about the FLTK basics. You should
first know how a callback works, before you can efficiently use fluid to
define callbacks - however the GUI layout is more intuitively done
directly in fluid.

Again a personal opinion that some other people share: I recommend doing
as much GUI design as useful/necessary in fluid, but define your classes
elsewhere (in independent .h and .cxx files) and call the class methods
in the callback - i.e. make the code entered directly in fluid minimal.
So you can seperate the UI design and functional code, which will be an
advantage in larger projects.

> Fluid for several reason especially
> for maintenance concerns seems to be the most suitable OS candidate
> available.

That's fine. We appreciate this.

> QtCreator/Designer looked good but it changes too often even
> with my limited playing about. Oddly it's also harder to get into than
> FLTK even though setting up signals is very easy. I've found that the
> docs can be out of date too. Not much fun for a beginner that has
> problems understanding them anyway. Must admit I feel there is too
> little if you want to do this type documentation about so some of the
> tutorial type web pages are very helpful.

In FLTK there's a very basic tutorial in one of the first chapters of
the docs. There are many example programs in test/*.cxx (together with
some fluid files in test/*.fl), and ever more in examples/*.cxx that are
worth a look.

There's also "Erco's Cheat Page" with many examples and fluid video
tutorials. This may help you going on with FLTK and particularly with fluid.

MacArthur, Ian (Selex ES, UK)

unread,
Oct 27, 2014, 11:39:02 AM10/27/14
to fltkg...@googlegroups.com

> One other aspect I missed out in the last post is that to make the code
> usable I have had to add a void function call to the end of the call
> back code. It takes the dial value and has no return value. It currently
> returns to the outer most widget as I have left that blank. At some
> point it will return to the code that does something about the dial
> setting change. I'm not clear if things will clear up correctly done
> this way.

I'm having trouble making sense of what you are saying here; you might have to make a little compilabale example to show us what you have; that might help clarify.


> If for instance these were pure subroutines at a low level I
> would be leaving stuff on the stack.

Um, no; I don't think that's likely here (in C or any higher level language, really).

The normal C / C++ scoping rules apply, and function calls and methods will unwind their stacks correctly. Unless you are doing something really weird in your function, there's no reason to think anything will be left on the stack.


> The sort of code organisation I see is "initialisation - change settings
> - run a process continuously" so dial changes in real terms are
> exceptions that always go back to change settings.

No, they are just function calls.

The fltk core is (in effect) sitting waiting for events from the window manager to arrive; when those events arrive, the fltk core then figures out which widget they are related to and runs whichever functions it has associated to those widgets, which will in turn call the callbacks you have registered for the widgets.

So it is not an "exception" in the C++ sense (nor in the somewhat different cpu hardware sense) this is all just C-style function calls.

> Hope my terminology
> is acceptable. I'm more used to dealing with "exceptions" via hardware
> interrupts usually in assembler..

As noted above, this is *not* all that much like handling an interrupt; this is much more like calling a function.

MacArthur, Ian (Selex ES, UK)

unread,
Oct 27, 2014, 11:49:00 AM10/27/14
to fltkg...@googlegroups.com
> Mmmm. Not sure if I should top or bottom post on here.

FWIW, I prefer plain text, edited for clarity (or brevity!), with responses interleaved...


> I tried all sorts of things with what I thought was the dials variable name
> using COUT and getting annoyed that I couldn't seem to get using namspace
> std to apply to all functions so had to keep retyping it for each cout as
I > was using it to test a number of things.. This included casting it etc.
> Thanks for the explanation of o I have found other information about that
> since asking - but not that one - its seems. I will get a grip of that
> eventually.

You might need to show us what your were doing; I'm not understanding enough about what you did to be able to comment on the problems you hit...


> The reason that I asked about function returns is that I do not know how the
> system handles them.

Normal C scoping rules apply...


> I asked the question because if have
> reworked the assembler output that some compilers can produce in the past so
> this area is a natural one for me to wonder about.

This is not like that...!

> There might have been some method of entering a function while destroying
> the one that was left.
> I do appreciate that there must be some mechanism to destroy it rather than
> just closing the window it's in. For all I know the size of the heap as PC
> people seem to call it might increase each time the dial setting is changed.

The heap and the stack are not the same thing.
Generally, objects on the heap may persist, objects on the stack will be unwound as the procedure calls return...

It sounds like you have some detailed low-level assembler experience, that is colouring your expectations of how higher-level languages behave - it'd probably all make a lot more sense if you got a little grounding in how the C-like languages deal with procedure calls and such, I'm sure it would all make so much more sense then.

John Longer

unread,
Oct 27, 2014, 5:07:03 PM10/27/14
to fltkg...@googlegroups.com, ian.ma...@selex-es.com
As I expected the names put into the C++ panel in Fluid I was using something like this with just about every thing I could think of in the commented out part

# data file for the Fltk User Interface Designer (fluid)
version 1.0302
header_name {.h}
code_name {.cxx}
Function {dialValue_CB(Fl_Widget*,void*)} {open return_type {static void}
} {
code {printf (dialValue "\\n");
showDialValue=dialValue;} {selected // this generates different numbers each time it's run
// so is likely to be a pointer

// Tried adding these and many other here.
// double value = *dialValue
// showDialValue = dialValue
// and many variants including ->
// all produced type convesion errors


}
}

Function {} {open
} {
Fl_Window {} {open
xywh {605 255 395 330} type Double visible
} {
Fl_Dial dialValue {
callback dialValue_CB
xywh {135 155 95 95} labeltype NO_LABEL
}
Fl_Value_Output showDialValue {
label {value:}
xywh {135 101 98 24} labeltype NO_LABEL
}
}
}

A bit facile of me as I realised that these were pointers but Fluid looks after that so I expected the code it generates to make any needed changes. The "variables" if that is what they are done this way default to global. I would be inclined to think static too but would need to find out. They would as I see it need to be static so that the actual function that produced them could then be destroyed once they have run. :-) Must admit I began to  fall out with ms's C compiler when is stopped making all variable declared out of main static. Mostly coding to test other systems. Nothing very dramatic. Not long after I fell out with using PC's for this - coding time interfered with my main job and there are other ways.

On the other aspect regarding functions. Eco's video states that all call back functions must have static void return types. Taking code as per the Fluid file Ian posted I added a function call to the end of the call back code and left the return type blank which is stated to cause it to return to the outer most widget. I did this because I eventually want this function to be a section of code that updates that particular dial readings and then falls through to the actual process. As this process loops endlessly there never will be any sort of return and dials will be changed again. Normally in my area I would have to signal a value change and test in the loop and react accordingly. Most of my coding of this type of process I have done has been run on a Harvard architecture. On that I would have to generate a signal or include scanning all dial values within the loop. I suspect I need to here as well but ... I don't know and the system that is running it could use suitable mechanisms that don't need this sort of approach. I will look at the C++ and see if I can see anything from that.

I had looked at all of the samples running but in this area I noticed that the valuator one didn't update the value output widget. I didn't realise that I needed to run them from konsole (KDE) to see any output. Now I have looked at the actual code I see a another good example of the use of o. The resize one is not much help in some ways. One of the reason I chose dials rather than sliders is that resizing needs to keep them round so am interested in constant aspect ratio resizing. This example mentions the worries I had about overloading resizes but I'm not keen on the way the cursor drifts off the buttons in the example. I had thought abut using a thumb wheel. I suspect I will look to see if a mouse drag can be trapped and use that - just as an alternative as the cursor is likely to remain in the window.

Sorry about the PC comment - comes from mostly coding systems that have no heap and I have even had to guess what that means. I've also had the miss fortune to have to work under the MISRA guidelines at times and most of my code that has run on a PC is pretty noddy stuff. I did manage to use a dma controller before MS did but that sort of thing is more my field.

John
-



Reply all
Reply to author
Forward
0 new messages